Skip to main content

Machine-to-machine API key authentication for Django REST Framework

Project description

django_machine_auth

PyPI

API key authentication for Django REST Framework with module-scoped permissions (similar to how platforms like GitHub, Stripe, or OpenAI expose scoped API keys).

Current version: 0.3.1 — relaxed custom-action validation (opt-in strict mode).

Documentation map

Guide Audience
setup.md Full step-by-step setup (recommended for first integration)
This README Quick reference and viewset overview

Topics in setup.md

  1. Installation and settings
  2. Part I — Define permissions in code
  3. Part II — Protect your APIs (MachineAuthViewSet)
  4. Part III — Permission catalog API (MachinePermissionViewSet)
  5. Part IV — API key management
  6. Part V — Call protected APIs with an API key
  7. Part VII — Request logs API
  8. Logging, commands, troubleshooting

Viewsets at a glance

The package provides four viewsets for four different jobs. Do not mix them up.

Viewset When to use Inherit in your project? Auth on requests
MachineAuthViewSet Protect your business APIs (integrations call these) Yes Authorization: machine_auth <key>
MachinePermissionViewSet List permissions for assignment UI (dropdowns) Optional (subclass to filter) JWT / session (your DRF auth)
MachineAPIKeyManagementViewSet Create/list/update/revoke keys Usually no (use package URLs) JWT / session (your DRF auth)
MachineAPIKeyRequestLogViewSet List/inspect machine-auth request logs Usually no (use package URLs) JWT / session (your DRF auth)

Import from:

from django_machine_auth.views import (
    MachineAuthViewSet,
    MachinePermissionViewSet,
    MachineAPIKeyManagementViewSet,
    MachineAPIKeyRequestLogViewSet,
)

Quick start

1. Install and configure

pip install django-machine-auth
INSTALLED_APPS = ["django_machine_auth", ...]

MACHINE_AUTH = {
    "KEY_PREFIX": "mac_",
    "ENABLE_REQUEST_LOGGING": False,
    "LOGGING_MODE": "redacted",
    "CACHE_TIMEOUT": 3600,
    "STRICT_ACTION_VALIDATION": False,  # True = require all @actions in api_key_perm.py
}

REST_FRAMEWORK = {
    "DEFAULT_THROTTLE_RATES": {
        "machine_api_key": "1000/hour",
    },
}

2. Wire management URLs (one line)

path("machine-auth/", include("django_machine_auth.urls")),

3. Define permissions in code

your_app/api_key_perm.py:

from django_machine_auth.decorators import api_key_module


@api_key_module("complaint", label="Complaint")
class ComplaintModule:
    crud = ["view", "create", "update", "delete"]
    actions = {"export": ["get"]}

Then:

python manage.py machine_auth_sync

4. Protect your API — inherit MachineAuthViewSet

from rest_framework import mixins
from rest_framework.response import Response
from django_machine_auth.views import MachineAuthViewSet


class ComplaintMachineViewSet(MachineAuthViewSet, mixins.ListModelMixin):
    module = "complaint"  # required — must match api_key_perm.py

    def get_queryset(self):
        return Complaint.objects.filter(...)

    def list(self, request):
        return Response(ComplaintSerializer(self.get_queryset(), many=True).data)

Register on your router (e.g. /v1/complaint/machine/).

5. Create keys and call your API

Management (JWT/session):

POST /machine-auth/machine-api-keys/

Integration call:

GET /v1/complaint/machine/
Authorization: machine_auth mac_xxxxx

MachineAuthViewSet (protect your APIs)

Purpose: Endpoints that external systems call using an API key.

What it provides automatically:

  • MachineAPIKeyAuthentication
  • MachineAuthPermission (checks key’s permission list vs action + HTTP method)
  • MachineAPIKeyRateThrottle

You must:

  1. Set module = "<name>" matching @api_key_module("<name>").
  2. Add DRF mixins (ListModelMixin, etc.) and implement actions.
  3. Declare custom @action names in api_key_perm.py under actions (required when STRICT_ACTION_VALIDATION is True; optional otherwise).
  4. If mixing with another base viewset (e.g. JWT), put MachineAuthViewSet first in inheritance, or set authentication_classes / permission_classes explicitly on the combined class.

Custom actions not in api_key_perm.py: With default STRICT_ACTION_VALIDATION: False, undeclared @actions only require a valid API key (no scoped permission). Set STRICT_ACTION_VALIDATION: True for strict catalog + permission checks on every custom action.

Permission mapping:

DRF action Required permission
list, retrieve module.view
create module.create
update, partial_update module.update
destroy module.delete
custom @action module.<action>.<method_lower>

After auth: request.machine_api_key is set; use request.machine_api_key.permissions if needed.

Details: setup.md — Part II.


MachinePermissionViewSet (permission catalog)

Purpose: Return assignable permissions from the database (for UI when creating/updating keys).

Default route (no subclass needed):

  • GET /machine-auth/permissions/
  • GET /machine-auth/permissions/?module=complaint
  • GET /machine-auth/permissions/?search=export

Restrict what users can assign — subclass and override get_queryset():

from django_machine_auth.views import MachinePermissionViewSet


class ComplaintPermissionViewSet(MachinePermissionViewSet):
    def get_queryset(self):
        return super().get_queryset().filter(module="complaint")

Register the subclass on your router only if you need a custom path; otherwise use built-in filters.

Details: setup.md — Part III.


API key management

Purpose: Create, list, inspect, update, and revoke API keys (admin portal / internal API).

Use package URLs (recommended):

Method Path
GET /machine-auth/permissions/
GET /machine-auth/machine-api-keys/
POST /machine-auth/machine-api-keys/
GET /machine-auth/machine-api-keys/{id}/
PATCH /machine-auth/machine-api-keys/{id}/
POST /machine-auth/machine-api-keys/{id}/deactivate/

Access:

  • Superuser: all keys; create for any user; list filter ?user=<id>.
  • Authenticated user: own keys only; create only for self.

raw_api_key is returned once on create. List/retrieve never expose hashed_key.

Details: setup.md — Part IV.


Request logs API

Purpose: Browse audit logs for machine-authenticated API calls (SuperAdmin portal or user self-service).

Prerequisites: ENABLE_REQUEST_LOGGING = True and MachineAuthLoggingMiddleware installed.

Method Path
GET /machine-auth/request-logs/
GET /machine-auth/request-logs/{id}/

Access:

  • Superuser: all logs; ?user=<id>, ?api_key=<id>.
  • Authenticated user: logs for API keys they own; ?api_key=<id> (403 if not their key).

List returns metadata only (method, url, status, duration, etc.). Detail includes headers, request_body, response_body when stored.

Details: setup.md — Part VII.


Three “permission” concepts (do not confuse)

Concept Where it lives Used for
Module definition api_key_perm.py in your app Source of truth in code
Permission registry MachinePermission table Validation + catalog API
Key permissions MachineAPIKey.permissions JSON Runtime checks on protected APIs

Flow: codemachine_auth_syncDB → assign on key → MachineAuthViewSet enforces on each request.


Logging

MIDDLEWARE = [
    "django_machine_auth.middleware.logging_middleware.MachineAuthLoggingMiddleware",
]
MACHINE_AUTH = {"ENABLE_REQUEST_LOGGING": True, "LOGGING_MODE": "redacted"}

Only requests with successful machine auth context are logged.


Management commands

python manage.py machine_auth_sync          # DB ← code permissions
python manage.py machine_auth_permissions   # print permission docs

Troubleshooting

  • 403 on protected API: key missing exact permission string (e.g. complaint.export.get).
  • 401: invalid/expired/inactive key or wrong KEY_PREFIX.
  • Module not registered: view.module must match api_key_perm.py; run sync.
  • Throttle error: set machine_api_key in DEFAULT_THROTTLE_RATES.
  • Logging empty: middleware enabled + valid machine auth on request.
  • Log API 403 on ?api_key=: key belongs to another user.
  • Startup error for undeclared @action: set STRICT_ACTION_VALIDATION: False (default) or add the action to api_key_perm.py.

More

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_machine_auth-0.3.1.tar.gz (36.4 kB view details)

Uploaded Source

Built Distribution

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

django_machine_auth-0.3.1-py3-none-any.whl (31.0 kB view details)

Uploaded Python 3

File details

Details for the file django_machine_auth-0.3.1.tar.gz.

File metadata

  • Download URL: django_machine_auth-0.3.1.tar.gz
  • Upload date:
  • Size: 36.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for django_machine_auth-0.3.1.tar.gz
Algorithm Hash digest
SHA256 869886b4a042efb9910dbdd214e5271a014223dcf8a6d6050f0a380386ecb466
MD5 720b26ea6a9ec61d03e23b9f303d1776
BLAKE2b-256 98fefe5f94a7b5af16e64b4518ec679a745b0176d3d943b2eb8cb9796b90a859

See more details on using hashes here.

File details

Details for the file django_machine_auth-0.3.1-py3-none-any.whl.

File metadata

File hashes

Hashes for django_machine_auth-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 e45afa6d39da757888618f9bade38944017c1ef247ce9a1bedf8422465d31aa3
MD5 655ccacf7a259badb8f870b12e56a4c4
BLAKE2b-256 28dac7e94c35b761f25220b5bcd22165892ad95d74706454e771dd798cfe2435

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