django-trusted-devices


Namedjango-trusted-devices JSON
Version 1.3 PyPI version JSON
download
home_pageNone
SummarySecure and manage trusted login devices for Django users
upload_time2025-08-14 11:24:00
maintainerNone
docs_urlNone
authorNone
requires_python>=3.10
licenseMIT
keywords django trusted devices user security sessions login djangorestframework
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # πŸ” 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.

---
[![Docs](https://img.shields.io/badge/docs-view-green?style=for-the-badge&logo=readthedocs)](https://ganiyevuz.github.io/django-trusted-devices/)


## πŸš€ Features

* πŸ”‘ **JWT tokens** include a unique `device_uid`
* 🌍 **Auto-detect IP, region, and city** via [ipapi.co](https://ipapi.co)
* πŸ›‘οΈ **Per-device session tracking** with update/delete restrictions
* πŸ”„ **Custom** `TokenObtainPair`, `TokenRefresh`, and `TokenVerify` views
* πŸšͺ **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_DEVICE` Django settings
* 🚫 **Rejects refresh/verify** from unknown or expired devices

---

## πŸ“¦ Installation

```bash
pip install django-trusted-device
```

Add to your `INSTALLED_APPS`:

```python
INSTALLED_APPS = [
    ...
    'trusted_devices',
    'rest_framework_simplejwt.token_blacklist',
]
```

Run migrations:

```bash
python manage.py migrate
```

---

## βš™οΈ Configuration

Customize behavior in `settings.py`:

```python
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.:

```python
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:

```python
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`:

```python
from trusted_devices.views import TrustedDeviceViewSet

router.register(r'trusted-devices', TrustedDeviceViewSet, basename='trusted-device')
```

Endpoints:

* `GET /trusted-devices` β€” List all trusted devices
* `DELETE /trusted-devices/{device_uid}` β€” Delete a device
* `PATCH /trusted-devices/{device_uid}` β€” Update device permissions

---

## πŸ‘€ Device Model

Each trusted device includes:

* `device_uid`: Unique UUID
* `user_agent`: Browser or device string
* `ip_address`: IP address
* `country`, `region`, `city`: Geolocation (via `ipapi.co`)
* `last_seen`, `created_at`: Timestamps
* `can_delete_other_devices`, `can_update_other_devices`: Optional privileges

---

## 🧠 How It Works

1. During login, a `device_uid` is generated and embedded in the token.
2. Clients use that token (with `device_uid`) for refresh/verify.
3. Each request is linked to a known device.
4. Users can manage or restrict their devices via API or Admin.

---

## πŸ§ͺ Testing Locally

```bash
# 🧩 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](https://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:

1. **Fork** the repo and create a feature branch:

   ```bash
   git checkout -b feature/my-amazing-idea
   ```

2. **Follow code style** – run:

   ```bash
   make lint  # runs flake8, isort, black
   ```

3. **Write & run tests**:

   ```bash
   pytest
   ```

4. **Commit** with clear messages and open a **Pull Request**.
   GitHub Actions will lint + test your branch automatically.

---

### πŸ—£οΈ Discussions & Issues

* πŸ’‘ Questions / ideas β†’ [GitHub Discussions](https://github.com/ganiyevuz/django-trusted-devices/discussions)
* πŸ› Bugs / feature requests β†’ [GitHub Issues](https://github.com/ganiyevuz/django-trusted-devices/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 as `vX.Y.Z`

---

## πŸ“„ License

[MIT](LICENSE)

---

Made with ❀️ by [Jahongir Ganiev](https://github.com/ganiyevuz)
Security questions or commercial support? Open an issue or email **[contact@jakhongir.dev](mailto:contact@jakhongir.dev)**

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "django-trusted-devices",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.10",
    "maintainer_email": null,
    "keywords": "django, trusted devices, user security, sessions, login, djangorestframework",
    "author": null,
    "author_email": "Jakhongir Ganiev <contact@jakhongir.dev>",
    "download_url": "https://files.pythonhosted.org/packages/3e/91/10316b1d2b211d59f20eab39ae577ab71a38a0821505383ddca0b16b646f/django_trusted_devices-1.3.tar.gz",
    "platform": null,
    "description": "# \ud83d\udd10 Django Trusted Device\n\nA plug-and-play Django app that adds **trusted device management** to your API authentication system using\n`djangorestframework-simplejwt`. Automatically associates tokens with user devices, tracks login locations,\nand enables per-device control over access and session management.\n\n---\n[![Docs](https://img.shields.io/badge/docs-view-green?style=for-the-badge&logo=readthedocs)](https://ganiyevuz.github.io/django-trusted-devices/)\n\n\n## \ud83d\ude80 Features\n\n* \ud83d\udd11 **JWT tokens** include a unique `device_uid`\n* \ud83c\udf0d **Auto-detect IP, region, and city** via [ipapi.co](https://ipapi.co)\n* \ud83d\udee1\ufe0f **Per-device session tracking** with update/delete restrictions\n* \ud83d\udd04 **Custom** `TokenObtainPair`, `TokenRefresh`, and `TokenVerify` views\n* \ud83d\udeaa **Logout unwanted sessions** from the device list\n* \ud83e\uddfc **Automatic cleanup**, optional global control rules\n* \ud83e\udde9 **API-ready** \u2013 supports DRF out of the box\n* \u2699\ufe0f **Fully customizable** via `TRUSTED_DEVICE` Django settings\n* \ud83d\udeab **Rejects refresh/verify** from unknown or expired devices\n\n---\n\n## \ud83d\udce6 Installation\n\n```bash\npip install django-trusted-device\n```\n\nAdd to your `INSTALLED_APPS`:\n\n```python\nINSTALLED_APPS = [\n    ...\n    'trusted_devices',\n    'rest_framework_simplejwt.token_blacklist',\n]\n```\n\nRun migrations:\n\n```bash\npython manage.py migrate\n```\n\n---\n\n## \u2699\ufe0f Configuration\n\nCustomize behavior in `settings.py`:\n\n```python\nTRUSTED_DEVICE = {\n    \"DELETE_DELAY_MINUTES\": 60 * 24 * 7,  # 7 days\n    \"UPDATE_DELAY_MINUTES\": 60,           # 1 hour\n    \"ALLOW_GLOBAL_DELETE\": True,\n    \"ALLOW_GLOBAL_UPDATE\": True,\n}\n```\n\n---\n\n## \ud83e\udde9 Usage\n\n\n## \ud83d\udd10 SimpleJWT configuration\n\nReplace default SimpleJWT serializers with TrustedDevice serializers.:\n\n```python\nfrom datetime import timedelta\n\nREST_FRAMEWORK = {\n    'DEFAULT_AUTHENTICATION_CLASSES': (\n        'trusted_devices.authentication.TrustedDeviceAuthentication',\n    ),\n}\n\nSIMPLE_JWT = {\n    \"ACCESS_TOKEN_LIFETIME\": timedelta(minutes=60),\n    \"REFRESH_TOKEN_LIFETIME\": timedelta(days=30),\n    \"AUTH_HEADER_TYPES\": (\"Bearer\",),\n    \"TOKEN_OBTAIN_SERIALIZER\": 'trusted_devices.serializers.TrustedDeviceTokenObtainPairSerializer',\n    \"TOKEN_REFRESH_SERIALIZER\": 'trusted_devices.serializers.TrustedDeviceTokenRefreshSerializer',\n    \"TOKEN_VERIFY_SERIALIZER\": 'trusted_devices.serializers.TrustedDeviceTokenVerifySerializer',\n}\n\n```\n\n### \ud83d\udd10 Custom Token Views\n\nReplace the default SimpleJWT views with:\n\n```python\nfrom trusted_devices.views import (\n    TrustedDeviceTokenObtainPairView,\n    TrustedDeviceTokenRefreshView,\n    TrustedDeviceTokenVerifyView,\n)\n\nurlpatterns = [\n    path('api/token/', TrustedDeviceTokenObtainPairView.as_view(), name='token_obtain_pair'),\n    path('api/token/refresh/', TrustedDeviceTokenRefreshView.as_view(), name='token_refresh'),\n    path('api/token/verify/', TrustedDeviceTokenVerifyView.as_view(), name='token_verify'),\n]\n```\n\n---\n\n### \ud83d\udce1 Device Management API\n\nUse the provided `TrustedDeviceViewSet`:\n\n```python\nfrom trusted_devices.views import TrustedDeviceViewSet\n\nrouter.register(r'trusted-devices', TrustedDeviceViewSet, basename='trusted-device')\n```\n\nEndpoints:\n\n* `GET /trusted-devices` \u2014 List all trusted devices\n* `DELETE /trusted-devices/{device_uid}` \u2014 Delete a device\n* `PATCH /trusted-devices/{device_uid}` \u2014 Update device permissions\n\n---\n\n## \ud83d\udc64 Device Model\n\nEach trusted device includes:\n\n* `device_uid`: Unique UUID\n* `user_agent`: Browser or device string\n* `ip_address`: IP address\n* `country`, `region`, `city`: Geolocation (via `ipapi.co`)\n* `last_seen`, `created_at`: Timestamps\n* `can_delete_other_devices`, `can_update_other_devices`: Optional privileges\n\n---\n\n## \ud83e\udde0 How It Works\n\n1. During login, a `device_uid` is generated and embedded in the token.\n2. Clients use that token (with `device_uid`) for refresh/verify.\n3. Each request is linked to a known device.\n4. Users can manage or restrict their devices via API or Admin.\n\n---\n\n## \ud83e\uddea Testing Locally\n\n```bash\n# \ud83e\udde9 Create and activate a uv-managed virtual environment\nuv venv\nsource .venv/bin/activate  # Windows: .venv\\Scripts\\activate\n\n# \ud83d\udce6 Install the package in editable mode with dev extras\nuv pip install -e \".[dev]\"\n\n# \ud83e\uddea Run the test suite\npytest\n```\n\n---\n\n## \ud83e\uddf1 Dependencies\n\n* Django\n* Django REST Framework\n* djangorestframework-simplejwt\n* [ipapi.co](https://ipapi.co) (for IP geolocation)\n\n---\n\n## \ud83d\uddc3\ufe0f Model Snapshot\n\n| Field                      | Purpose             |\n| -------------------------- | ------------------- |\n| `device_uid`               | UUID primary key    |\n| `user_agent`, `ip_address` | Device fingerprint  |\n| `country / region / city`  | Geo\u2011lookup          |\n| `last_seen / created_at`   | Activity timestamps |\n| `can_update_other_devices` | Granular permission |\n| `can_delete_other_devices` | Granular permission |\n\n---\n\n## \ud83e\udd1d Collaboration & Contributing\n\nWe love community contributions! To collaborate:\n\n1. **Fork** the repo and create a feature branch:\n\n   ```bash\n   git checkout -b feature/my-amazing-idea\n   ```\n\n2. **Follow code style** \u2013 run:\n\n   ```bash\n   make lint  # runs flake8, isort, black\n   ```\n\n3. **Write & run tests**:\n\n   ```bash\n   pytest\n   ```\n\n4. **Commit** with clear messages and open a **Pull Request**.\n   GitHub Actions will lint + test your branch automatically.\n\n---\n\n### \ud83d\udde3\ufe0f Discussions & Issues\n\n* \ud83d\udca1 Questions / ideas \u2192 [GitHub Discussions](https://github.com/ganiyevuz/django-trusted-devices/discussions)\n* \ud83d\udc1b Bugs / feature requests \u2192 [GitHub Issues](https://github.com/ganiyevuz/django-trusted-devices/issues)\n\n---\n\n### \ud83d\udee0 Maintainer Workflow\n\n* PRs require at least one approval and passing CI\n* We **squash\u2011merge** to keep history clean\n* Follows **Semantic Versioning** (`MAJOR.MINOR.PATCH`), tagged as `vX.Y.Z`\n\n---\n\n## \ud83d\udcc4 License\n\n[MIT](LICENSE)\n\n---\n\nMade with \u2764\ufe0f by [Jahongir Ganiev](https://github.com/ganiyevuz)\nSecurity questions or commercial support? Open an issue or email **[contact@jakhongir.dev](mailto:contact@jakhongir.dev)**\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Secure and manage trusted login devices for Django users",
    "version": "1.3",
    "project_urls": null,
    "split_keywords": [
        "django",
        " trusted devices",
        " user security",
        " sessions",
        " login",
        " djangorestframework"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "a82cd8f20d3d64c7309b03fa87ed2e3a08acb73de85e52a8586742a5eec24208",
                "md5": "5c507f78f124f8ba3fcdcb5fc48037f4",
                "sha256": "5f9578f4c816c7bb3f44e6a1c6b7e45de5117b7d53cdafa6bfbcad500aeee235"
            },
            "downloads": -1,
            "filename": "django_trusted_devices-1.3-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "5c507f78f124f8ba3fcdcb5fc48037f4",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.10",
            "size": 16752,
            "upload_time": "2025-08-14T11:23:59",
            "upload_time_iso_8601": "2025-08-14T11:23:59.438958Z",
            "url": "https://files.pythonhosted.org/packages/a8/2c/d8f20d3d64c7309b03fa87ed2e3a08acb73de85e52a8586742a5eec24208/django_trusted_devices-1.3-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "3e9110316b1d2b211d59f20eab39ae577ab71a38a0821505383ddca0b16b646f",
                "md5": "3dabc9e4059a4d258017ab3e166b2c0e",
                "sha256": "c3bb0f0c33fd1617f8e124d46a89bffdf04877bf40fe2916890b26aa9ba4d24e"
            },
            "downloads": -1,
            "filename": "django_trusted_devices-1.3.tar.gz",
            "has_sig": false,
            "md5_digest": "3dabc9e4059a4d258017ab3e166b2c0e",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.10",
            "size": 12762,
            "upload_time": "2025-08-14T11:24:00",
            "upload_time_iso_8601": "2025-08-14T11:24:00.233543Z",
            "url": "https://files.pythonhosted.org/packages/3e/91/10316b1d2b211d59f20eab39ae577ab71a38a0821505383ddca0b16b646f/django_trusted_devices-1.3.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-08-14 11:24:00",
    "github": false,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "lcname": "django-trusted-devices"
}
        
Elapsed time: 2.57774s