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.2 — silent fast startup for undeclared custom actions (defaults).

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
    "WARN_ON_UNDECLARED_ACTIONS": False,  # True = log startup warnings when strict is False
}

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 defaults (STRICT_ACTION_VALIDATION: False, WARN_ON_UNDECLARED_ACTIONS: False), undeclared @actions require only a valid API key, with no startup scan or warnings. Set STRICT_ACTION_VALIDATION: True for full enforcement, or WARN_ON_UNDECLARED_ACTIONS: True to log missing actions without failing startup.

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.
  • Startup warnings for undeclared @action: set WARN_ON_UNDECLARED_ACTIONS: False (default).

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.2.tar.gz (37.5 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.2-py3-none-any.whl (31.4 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for django_machine_auth-0.3.2.tar.gz
Algorithm Hash digest
SHA256 de350f111111ba94d1984e2684a74e75b77b50692dd94e7207d7619321bea514
MD5 524cf9cfc86f721b0a2c803f5ce87509
BLAKE2b-256 1782d5dfc2baaea97e6d88d3a486d043fb3605197bebeaa9e584e5a5794837f9

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for django_machine_auth-0.3.2-py3-none-any.whl
Algorithm Hash digest
SHA256 9171130272019f6dc7b39cbeea15ac4869f76c831e42780023b2ec9531ff7084
MD5 2b96a73aca320a69e6c12a3d6fd35084
BLAKE2b-256 58866381c0d9da9b372125ff63f2e1a8e1682b0b2a77d38651723e9020daff54

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