Secure and manage trusted login devices for Django users
Project description
🔐 Django Trusted Device
A plug-and-play Django app that adds trusted device management to your API authentication system using
djangorestframework-simplejwt. Automatically associates tokens with user devices, tracks login locations,
and enables per-device control over access and session management.
🚀 Features
- 🔑 JWT tokens include a unique
device_uid - 🌍 Auto-detect IP, region, and city via ipapi.co
- 🛡️ Per-device session tracking with update/delete restrictions
- 🔄 Custom
TokenObtainPair,TokenRefresh, andTokenVerifyviews - 🚪 Logout unwanted sessions from the device list
- 🧼 Automatic cleanup, optional global control rules
- 🧩 API-ready – supports DRF out of the box
- ⚙️ Fully customizable via
TRUSTED_DEVICEDjango settings - 🚫 Rejects refresh/verify from unknown or expired devices
📦 Installation
pip install django-trusted-device
Add to your INSTALLED_APPS:
INSTALLED_APPS = [
...
'trusted_devices',
'rest_framework_simplejwt.token_blacklist',
]
Run migrations:
python manage.py migrate
⚙️ Configuration
Customize behavior in settings.py:
TRUSTED_DEVICE = {
"DELETE_DELAY_MINUTES": 60 * 24 * 7, # 7 days
"UPDATE_DELAY_MINUTES": 60, # 1 hour
"ALLOW_GLOBAL_DELETE": True,
"ALLOW_GLOBAL_UPDATE": True,
}
🧩 Usage
🔐 SimpleJWT configuration
Replace default SimpleJWT serializers with TrustedDevice serializers.:
from datetime import timedelta
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'trusted_devices.authentication.TrustedDeviceAuthentication',
),
}
SIMPLE_JWT = {
"ACCESS_TOKEN_LIFETIME": timedelta(minutes=60),
"REFRESH_TOKEN_LIFETIME": timedelta(days=30),
"AUTH_HEADER_TYPES": ("Bearer",),
"TOKEN_OBTAIN_SERIALIZER": 'trusted_devices.serializers.TrustedDeviceTokenObtainPairSerializer',
"TOKEN_REFRESH_SERIALIZER": 'trusted_devices.serializers.TrustedDeviceTokenRefreshSerializer',
"TOKEN_VERIFY_SERIALIZER": 'trusted_devices.serializers.TrustedDeviceTokenVerifySerializer',
}
🔐 Custom Token Views
Replace the default SimpleJWT views with:
from trusted_devices.views import (
TrustedDeviceTokenObtainPairView,
TrustedDeviceTokenRefreshView,
TrustedDeviceTokenVerifyView,
)
urlpatterns = [
path('api/token/', TrustedDeviceTokenObtainPairView.as_view(), name='token_obtain_pair'),
path('api/token/refresh/', TrustedDeviceTokenRefreshView.as_view(), name='token_refresh'),
path('api/token/verify/', TrustedDeviceTokenVerifyView.as_view(), name='token_verify'),
]
📡 Device Management API
Use the provided TrustedDeviceViewSet:
from trusted_devices.views import TrustedDeviceViewSet
router.register(r'trusted-devices', TrustedDeviceViewSet, basename='trusted-device')
Endpoints:
GET /trusted-devices— List all trusted devicesDELETE /trusted-devices/{device_uid}— Delete a devicePATCH /trusted-devices/{device_uid}— Update device permissions
👤 Device Model
Each trusted device includes:
device_uid: Unique UUIDuser_agent: Browser or device stringip_address: IP addresscountry,region,city: Geolocation (viaipapi.co)last_seen,created_at: Timestampscan_delete_other_devices,can_update_other_devices: Optional privileges
🧠 How It Works
- During login, a
device_uidis generated and embedded in the token. - Clients use that token (with
device_uid) for refresh/verify. - Each request is linked to a known device.
- Users can manage or restrict their devices via API or Admin.
🧪 Testing Locally
# 🧩 Create and activate a uv-managed virtual environment
uv venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
# 📦 Install the package in editable mode with dev extras
uv pip install -e ".[dev]"
# 🧪 Run the test suite
pytest
🧱 Dependencies
- Django
- Django REST Framework
- djangorestframework-simplejwt
- ipapi.co (for IP geolocation)
🗃️ Model Snapshot
| Field | Purpose |
|---|---|
device_uid |
UUID primary key |
user_agent, ip_address |
Device fingerprint |
country / region / city |
Geo‑lookup |
last_seen / created_at |
Activity timestamps |
can_update_other_devices |
Granular permission |
can_delete_other_devices |
Granular permission |
🤝 Collaboration & Contributing
We love community contributions! To collaborate:
-
Fork the repo and create a feature branch:
git checkout -b feature/my-amazing-idea
-
Follow code style – run:
make lint # runs flake8, isort, black
-
Write & run tests:
pytest
-
Commit with clear messages and open a Pull Request. GitHub Actions will lint + test your branch automatically.
🗣️ Discussions & Issues
- 💡 Questions / ideas → GitHub Discussions
- 🐛 Bugs / feature requests → GitHub Issues
🛠 Maintainer Workflow
- PRs require at least one approval and passing CI
- We squash‑merge to keep history clean
- Follows Semantic Versioning (
MAJOR.MINOR.PATCH), tagged asvX.Y.Z
📄 License
Made with ❤️ by Jahongir Ganiev Security questions or commercial support? Open an issue or email contact@jakhongir.dev
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
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_trusted_devices-1.1.tar.gz.
File metadata
- Download URL: django_trusted_devices-1.1.tar.gz
- Upload date:
- Size: 12.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b9c10e2fc21866e254040d9abc950d4d32fbd1b6badc8fe60521bf52c7b7f929
|
|
| MD5 |
6df1c5aac6af108aa53f41c8051c32f2
|
|
| BLAKE2b-256 |
bc93227d45f492cc62d07f2c9216b63b34ff0fa8bc101ce51a5094ad03f71c7d
|
File details
Details for the file django_trusted_devices-1.1-py3-none-any.whl.
File metadata
- Download URL: django_trusted_devices-1.1-py3-none-any.whl
- Upload date:
- Size: 16.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fe3464ab5f2591301560ccf2c6cd8d12e689d75fc7a8f363f2795fc0497d3272
|
|
| MD5 |
c675b9d9bca0cbb1a8a63f8bb1b82416
|
|
| BLAKE2b-256 |
5ab100768d46675f74982b7836c6b855fe62c4e1d4f236cee16d17403606e34a
|