Skip to main content

RobustAuth

Modern session-based opaque token authentication for Django REST Framework.

RobustAuth replaces JWT complexity with secure, database-backed opaque tokens — giving you instant revocation, full session intelligence, and production-grade security controls out of the box.

PyPI version Django DRF Docs License: MIT


Why RobustAuth?

Feature DRF TokenAuth SimpleJWT RobustAuth
Instant token revocation ✅ ❌ (wait for expiry) ✅
Refresh token rotation ❌ ✅ ✅
Reuse detection ❌ ❌ ✅
Multi-device session tracking ❌ ❌ ✅
Single-session enforcement ❌ ❌ ✅
Login / audit history ❌ ❌ ✅
Device & browser detection ❌ ❌ ✅ (optional)
Hashed token storage ❌ N/A ✅
Sliding sessions ❌ ❌ ✅
Django admin integration ✅ ❌ ✅

Installation

pip install robustauth

# Optional: device/browser detection
pip install robustauth[device]

Add to INSTALLED_APPS:

INSTALLED_APPS = [
    ...
    "robustauth",
]

Run migrations:

python manage.py migrate

Add URL patterns:

# urls.py
from django.urls import path, include

urlpatterns = [
    path("auth/", include("robustauth.urls")),
    ...
]

Quick Start

1. Configure (optional — sensible defaults apply)

# settings.py
ROBUST_AUTH = {
    "ACCESS_TOKEN_TTL": 900,          # 15 min
    "REFRESH_TOKEN_TTL": 604800,      # 7 days
    "SESSION_POLICY": "single",       # one device at a time
    "ROTATE_REFRESH_TOKENS": True,
    "REVOKE_ON_PASSWORD_CHANGE": True,
    "TRACK_IPS": True,
    "TRACK_USER_AGENTS": True,
    "MAX_SESSIONS": 3,                # used when SESSION_POLICY = "max_count"
}

2. Set authentication on your views

# settings.py
REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "robustauth.authentication.RobustTokenAuthentication",
    ],
}

Or per-view:

from robustauth.authentication import RobustTokenAuthentication

class MyView(APIView):
    authentication_classes = [RobustTokenAuthentication]
    permission_classes = [IsAuthenticated]

API Endpoints

All endpoints are prefixed with wherever you mounted robustauth.urls.

POST /auth/login/

Authenticate a user and receive token pair.

Request:

{
  "username": "alice",
  "password": "secret"
}

Response 200:

{
  "access_token": "a3f9...",
  "refresh_token": "b7c2...",
  "session_id": "550e8400-e29b-...",
  "token_type": "Bearer"
}

POST /auth/token/refresh/

Exchange a refresh token for a new token pair. The old refresh token is invalidated.

Request:

{ "refresh_token": "b7c2..." }

Response 200:

{
  "access_token": "d1e4...",
  "refresh_token": "f5a8...",
  "token_type": "Bearer"
}

POST /auth/logout/

Revoke the current session. Requires Authorization: Bearer <access_token>.

Response 200:

{ "detail": "Logged out successfully." }

POST /auth/logout/all/

Revoke all sessions (optionally keep the current one alive).

Request:

{ "keep_current": true }

GET /auth/sessions/

List all active sessions for the authenticated user.

Response:

[
  {
    "id": "550e8400-...",
    "state": "active",
    "ip_address": "41.58.12.34",
    "device_type": "mobile",
    "os_family": "Android",
    "browser_family": "Chrome Mobile",
    "device_name": "Samsung SM-G991",
    "created_at": "2024-01-15T10:23:00Z",
    "last_activity": "2024-01-15T14:05:00Z",
    "is_current": true
  }
]

DELETE /auth/sessions/<uuid>/

Revoke a specific session by ID.


GET /auth/history/

Return the authentication event log for the authenticated user.

Query params: ?limit=50

Response:

[
  {
    "id": 1,
    "event": "login_success",
    "ip_address": "41.58.12.34",
    "user_agent": "Mozilla/5.0 ...",
    "extra": {},
    "timestamp": "2024-01-15T10:23:00Z"
  },
  ...
]

Session Policies

Control concurrent login behaviour via SESSION_POLICY:

Value Behaviour
"multi" Unlimited concurrent sessions (default)
"single" Only one active session per user — new login revokes all others
"max_count" At most MAX_SESSIONS active sessions; oldest revoked when limit hit
ROBUST_AUTH = {
    "SESSION_POLICY": "max_count",
    "MAX_SESSIONS": 3,
    "REVOKE_OLDEST_ON_LIMIT": True,   # False → raise error instead
}

Signals

Hook into authentication events in your own apps:

from robustauth import signals
from django.dispatch import receiver

@receiver(signals.token_reuse_detected)
def alert_security(sender, user, session, ip_address, **kwargs):
    # Send email, create alert, block IP, etc.
    SecurityAlert.objects.create(user=user, ip=ip_address)

@receiver(signals.brute_force_threshold_hit)
def handle_brute_force(sender, username, ip_address, failure_count, **kwargs):
    BlockedIP.objects.get_or_create(ip=ip_address)

@receiver(signals.user_logged_in)
def on_login(sender, user, session, **kwargs):
    send_new_login_email(user, session)

@receiver(signals.session_limit_reached)
def on_limit(sender, user, active_sessions, **kwargs):
    notify_user(user, "You've reached the maximum number of active sessions.")

Available signals:

Signal kwargs
user_logged_in user, session
user_logged_out user, session
token_reuse_detected user, session, ip_address
session_limit_reached user, active_sessions
brute_force_threshold_hit username, ip_address, failure_count

Password Change Integration

Automatically revoke all other sessions on password change:

from robustauth.session_manager import SessionManager

def change_password_view(request):
    # ... your password change logic ...
    user.set_password(new_password)
    user.save()

    # Revoke all sessions except the current one
    current_session = request.auth.session  # if using RobustTokenAuthentication
    SessionManager.on_password_change(user, current_session=current_session)

Middleware (optional)

Attach request.robust_session to all requests:

MIDDLEWARE = [
    ...
    "robustauth.middleware.RobustAuthMiddleware",
]

Then in any view:

def my_view(request):
    session = request.robust_session  # Session object or None

Cleanup

Remove expired tokens and prune history. Add to your cron or Celery beat:

python manage.py robustauth_cleanup

# Preview without deleting
python manage.py robustauth_cleanup --dry-run

Example Celery beat config:

CELERY_BEAT_SCHEDULE = {
    "robustauth-cleanup": {
        "task": "your_app.tasks.robustauth_cleanup",
        "schedule": crontab(hour=3, minute=0),  # daily at 3am
    }
}

Configuration Reference

Setting Default Description
ACCESS_TOKEN_TTL 900 Access token lifetime in seconds
REFRESH_TOKEN_TTL 604800 Refresh token lifetime in seconds
SESSION_POLICY "multi" "single" / "multi" / "max_count"
MAX_SESSIONS 5 Max concurrent sessions (max_count policy)
REVOKE_OLDEST_ON_LIMIT True Auto-revoke oldest when limit hit
ROTATE_REFRESH_TOKENS True Issue new refresh token on each use
REFRESH_TOKEN_REUSE_DETECTION True Revoke family + all sessions on reuse
REVOKE_ON_PASSWORD_CHANGE True Revoke all sessions on password change
HASH_TOKENS True Store sha256(token) instead of plaintext
TOKEN_BYTES 32 Random bytes for token generation
TRACK_IPS True Store client IP on sessions
TRACK_USER_AGENTS True Store User-Agent header
TRACK_DEVICE_INFO True Parse OS/browser from UA (ua-parser required)
STORE_LOGIN_HISTORY True Log successful logins
STORE_LOGOUT_HISTORY True Log logouts
STORE_FAILED_LOGINS True Log failed login attempts
MAX_HISTORY_ENTRIES 100 Max history rows kept per user
FAILED_LOGIN_THRESHOLD 5 Failures before brute_force_threshold_hit fires
FAILED_LOGIN_WINDOW 300 Window in seconds for counting failures
SLIDING_SESSION False Reset access token expiry on activity
SLIDING_SESSION_TTL 1800 Inactivity timeout for sliding sessions

Running Tests

pip install -e ".[dev]"
pytest

Changelog

0.1.0

  • Initial release
  • Opaque token authentication with hashed storage
  • Access + refresh token pair with rotation
  • Refresh token reuse detection with family revocation
  • Session policies: single / multi / max_count
  • Device and browser fingerprinting
  • Login / logout / failed login history
  • Brute-force threshold signal
  • Sliding sessions
  • Password change revocation
  • Django admin integration
  • robustauth_cleanup management command

License

MIT — see LICENSE.

Metadata

Release files for robustauth 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for robustauth 0.1.1
File Size Uploaded
robustauth-0.1.1.tar.gz 30.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for robustauth 0.1.1
File Interpreter ABI Platform
robustauth-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 61.7 kB

Release files / robustauth-0.1.1.tar.gz

Download URL robustauth-0.1.1.tar.gz
Size 30.7 kB
Tags Source
SHA-256 checksum
How to use checksums
4f19983c558ff8bd744b0a279866379f628b30159d4f7a999fa436816ca8d637
BLAKE2b-256 checksum
How to use checksums
6deaefd2913f5c286e65d7ec722f95f070a1a4bf46efd96690d47d2f7352c90e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 3, 2026.

Transparency log

Release files / robustauth-0.1.1-py3-none-any.whl

Download URL robustauth-0.1.1-py3-none-any.whl
Size 31.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a4fbe679ba966ec4743824450bb58765d66de6b5d375b4fbfc3e4df7ce2a8023
BLAKE2b-256 checksum
How to use checksums
d782483950cfc5c6f2db0ec4dadbcf8929b362f115f33ee94f69ebae7e8fdbe8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 3, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page