djangorestframework-api-key

repository·master·Indexed 20 days ago

https://github.com/florimondmanca/djangorestframework-api-key

A library providing API key permissions for the Django REST Framework, designed for secure machine-to-machine communication and server-side clients. It enables blocking anonymous traffic, implementing API key-based throttling, and authorizing internal services. The library features hashed key storage for security, support for custom API key models and managers, and flexible header parsing options.

Tokens
7K
Snippets
26
Records
32
Agent score
69%

What's inside djangorestframework-api-key

  1. What is djangorestframework-api-key and when to use it?

    master

    This library provides API key permissions for the Django REST Framework, designed for server-side clients (machines/third-party services) that do not have user accounts but need secure API access.

    Use Cases

    • Blocking anonymous traffic.
    • Implementing API key-based throttling.
    • Identifying usage patterns by logging request information alongside the API key.
    • Authorizing internal services (e.g., an API server or internal frontend application).

    Important Security Warnings

    • NOT for User Authentication: Do NOT use this package to identify individual users. For server-to-server authentication involving users, consider OAuth (e.g., django-oauth-toolkit).
    • HTTPS Required: Ensure you are serving your API over HTTPS to protect keys.
    • Key Security: API keys are hashed before storage and are only visible at the moment of creation.
  2. Understand the switch to SHA512 for API key hashing

    master

    Starting with version 3.0, the library switched from using Django's PASSWORD_HASHERS (which are slow and designed for low-entropy passwords) to using SHA512. This change is intended to improve performance, as API keys are high-entropy strings and do not require the computational overhead of password hashers.

    Key details:

    • Performance: API key verification is expected to be at least 10x faster.
    • Migration: Existing API key hashes will be transparently updated to SHA512 the next time .is_valid() is called (i.e., when the API key is used in a request).
    • Action required: Generally, no manual migration is needed, but it is recommended to test in a staging environment to ensure compatibility with your existing keys.
  3. How access is granted via API keys

    master

    For a request to be authorized using an API key, three conditions must be met:

    1. Header Presence: The configured API key header must be present in the request and correctly formatted.
    2. Prefix Match: A usable API key with the matching prefix must exist in the database.
    3. Hash Validation: The sha512 hash of the provided key must match the hash stored in the database.

    By default, only unrevoked keys are considered usable. You can customize header parsing or key management (e.g., using custom managers) to change this behavior.

  4. Understand the API key generation and storage scheme

    master

    An API key is a composite string formatted as P.SK, where P is an 8-character prefix and SK is a 32-character secret key.

    For security, the package treats these keys like passwords:

    • Storage: Only a sha512 hash of the key is stored in the database.
    • Visibility: The full generated key is displayed to the client only once at the time of creation. If lost, it cannot be retrieved from the database.

    Note: If you are upgrading from an older version of this module, keys stored with Django's standard PASSWORD_HASHERS will be automatically upgraded to sha512 when they are next used.

    GK = P.SK
    (Prefix of 8 chars + '.' + Secret of 32 chars)
  5. Review `.has_object_permission()` behavior on DRF 3.14+ with custom API key models

    master

    In version 3.0, when using Django REST Framework (DRF) 3.14.0 or higher, the implementation of .has_object_permission() on BaseHasAPIKey that was redundant with .has_permission() has been removed.

    If you use custom API key models, be aware that calls to super().has_object_permission() will now return True (the DRF default) instead of re-validating the API key. If your logic relied on has_object_permission to perform API key validation, you must adjust your implementation to ensure validation occurs in .has_permission() instead.

  6. Make authorized requests using Authorization or Custom headers

    master

    Default Authorization Header

    By default, clients must provide the API key in the Authorization header using the Api-Key prefix: Authorization: Api-Key <API_KEY>

    Custom Header

    If you are already using the Authorization header for another authentication scheme (like Token authentication), you can use a custom header by setting API_KEY_CUSTOM_HEADER in settings.py.

    For example, setting API_KEY_CUSTOM_HEADER = "HTTP_X_API_KEY" requires the client to use: X-Api-Key: <API_KEY>

    # settings.py
    API_KEY_CUSTOM_HEADER = "HTTP_X_API_KEY"
  7. Migrate custom APIKey models to version 1.4

    master

    If you are using a custom API key model that inherits from AbstractAPIKey, you must manually add a migration to your application to support the new prefix and hashed_key fields introduced in version 1.4.

    Follow these steps:

    1. Create a new migration file in your app's migrations/ directory (e.g., xxxx_prefix_hashed_key.py, where xxxx is the next available migration ID).
    2. Copy the migration logic into this file, ensuring you update APP_NAME, MODEL_NAME, and DEPENDENCIES to match your project's configuration.
    3. Run the migration for your specific app.

    This applies to users upgrading from version 1.3.x to 1.4.

    python manage.py migrate <my_app>
  8. Register custom API key models in Django Admin

    master

    To manage custom API key models via the Django admin site, create a subclass of APIKeyModelAdmin and register it using the @admin.register decorator.

    from django.contrib import admin
    from rest_framework_api_key.admin import APIKeyModelAdmin
    from .models import OrganizationAPIKey
    
    @admin.register(OrganizationAPIKey)
    class OrganizationAPIKeyModelAdmin(APIKeyModelAdmin):
        # Example: adding organization name to list view and search
        list_display = [*APIKeyModelAdmin.list_display, "organization__name"]
        search_fields = [*APIKeyModelAdmin.search_fields, "organization__name"]
  9. Migrate from 0.x to 1.0

    master

    The 1.0 release of djangorestframework-api-key is incompatible with 0.x versions. It introduces a new API key generation and validation scheme that uses a single header instead of two.

    Warning: This migration will destroy all existing API keys. Because the cryptographic generation and validation methods have changed fundamentally, existing keys cannot be migrated or inferred. You should backup your existing API key data before proceeding so you can notify clients to use new keys once the migration is complete.

    # 1. Backup your data manually before proceeding
    
    # 2. Reset migrations (This destroys existing keys)
    python manage.py migrate rest_framework_api_key zero
    
    # 3. Upgrade the package
    pip install "djangorestframework-api-key==1.0.*"
    
    # 4. Run the new migrations
    python manage.py migrate rest_framework_api_key
  10. Migrate the built-in APIKey model to version 1.4

    master

    The 1.4 release introduces prefix and hashed_key fields to the API keys. If you are using the default APIKey model provided by the package, you can apply these changes by running the migration shipped with the package.

    This applies to users upgrading from version 1.3.x to 1.4.

    python manage.py migrate rest_framework_api_key
  11. Run the test project for djangorestframework-api-key

    master

    To run the included test project to see djangorestframework-api-key in action, follow these steps:

    1. Run migrations: This initializes the database (SQLite by default).
    2. Create a superuser: This allows you to access the Django admin interface to manage API keys.
    3. Start the development server.
    # Run migrations
    python test_project/manage.py migrate
    
    # Create a superuser
    python test_project/manage.py createsuperuser
    
    # Start the server
    python test_project/manage.py runserver