User Guide

Getting started

Installation

Install from PyPI:

pip install djangorestframework-api-key

Note: this package requires Python 3.6+, Django 2.0+ and Django REST Framework 3.8+.

Project setup

Add the app to your INSTALLED_APPS:

# settings.py

INSTALLED_APPS = [
  # ...
  "rest_framework",
  "rest_framework_api_key",
]

Run the included migrations:

python manage.py migrate

Setting permissions

The HasAPIKey permission class protects a view behind API key authorization.

You can set the permission globally:

# settings.py
REST_FRAMEWORK = {
    "DEFAULT_PERMISSION_CLASSES": [
        "rest_framework_api_key.permissions.HasAPIKey",
    ]
}

or on a per-view basis:

# views.py
from rest_framework.views import APIView
from rest_framework_api_key.permissions import HasAPIKey

class UserListView(APIView):
    permission_classes = [HasAPIKey]
    # ...

See also Setting the permission policy for more information on using permission classes in the Django REST Framework.

Tip

You can use the bitwise operators | and & to compose HasAPIKey with other permission classes and achieve more complex authorization behaviour.

For example, to require a valid API key or authentication credentials, use:

from rest_framework.permissions import IsAuthenticated
from rest_framework_api_key.permissions import HasAPIKey
# ...
permission_classes = [HasAPIKey | IsAuthenticated]

Making authorized requests

Authorization header

By default, clients must pass their API key via the Authorization header. It must be formatted as follows:

Authorization: Api-Key <API_KEY>

where <API_KEY> refers to the full generated API key (see Creating and managing API keys below).

To know under which conditions access is granted, please see Grant scheme.

If wanting to also customize the keyword used for parsing the Api-Key, please see API key Custom Keyword

Custom header

You can set the API_KEY_CUSTOM_HEADER setting to a non-None value to require clients to pass their API key in a custom header instead of the Authorization header.

This is useful if you plan to use API keys AND an authentication scheme which already uses the Authorization header (e.g. token-based authentication).

For example, if you set:

# settings.py
API_KEY_CUSTOM_HEADER = "HTTP_X_API_KEY"

then clients must make authorized requests using:

X-Api-Key: <API_KEY>

where <API_KEY> refers to the full generated API key.

Please refer to HttpRequest.META for more information on headers in Django.

Creating and managing API keys

Admin site

When it is installed, djangorestframework-api-key adds an "API Key Permissions" section to the Django admin site where you can create, view and revoke API keys.

Note

Upon creating an API key from the admin, the full API key is shown only once in a success message banner. This is what should be passed in authorization headers. After creation, only the prefix of the API key is shown in the admin site, mostly for identification purposes. If you lose the full API key, you'll need to regenerate a new one.

Programmatic usage

API keys can be created, viewed and revoked programmatically by manipulating the APIKey model.

Note

The examples below use the Django shell.

  • You can view and query APIKey like any other model. For example, to know the total number of API keys:
>>> from rest_framework_api_key.models import APIKey
>>> APIKey.objects.count()
42
  • If you wish to create an API key programmatically, you'll most likely want a one-time access to its generated key too. To do so, use the .create_key() method on the APIKey objects manager instead of .create():
>>> from rest_framework_api_key.models import APIKey
>>> api_key, key = APIKey.objects.create_key(name="my-remote-service")
>>> # Proceed with `api_key` and `key`...

Danger

To prevent leaking API keys, you must only give the key to the client that triggered its generation. In particular, do not keep any trace of it on the server.

  • To retrieve an APIKey instance based on its generated key (which is not stored in the database) use the .get_from_key() method on the APIKey objects manager instead of .get(). This is useful if you'd like to access an APIKey object from a view protected by a HasAPIKey permission.
from rest_framework.views import APIView
from rest_framework_api_key.models import APIKey
from rest_framework_api_key.permissions import HasAPIKey

from .models import Project

class ProjectListView(APIView):
    permission_classes = [HasAPIKey]

    def get(self, request):
        """Retrieve a project based on the request API key."""
        key = request.META["HTTP_AUTHORIZATION"].split()[1]
        api_key = APIKey.objects.get_from_key(key)
        project = Project.objects.get(api_key=api_key)

Customization

This package provides various customization APIs that allow you to extend its basic behavior.

API key models

If the built-in APIKey model doesn't fit your needs, you can create your own by subclassing AbstractAPIKey. This is particularly useful if you need to store extra information or link API keys to other models using a ForeignKey or a ManyToManyField.

Warning

Associating API keys to users, directly or indirectly, can present a security risk. See also: Should I use API keys?.

Example

Here's how you could link API keys to an imaginary Organization model:

# organizations/models.py
from django.db import models
from rest_framework_api_key.models import AbstractAPIKey

class Organization(models.Model):
    name = models.CharField(max_length=128)
    active = models.BooleanField(default=True)

class OrganizationAPIKey(AbstractAPIKey):
    organization = models.ForeignKey(
        Organization,
        on_delete=models.CASCADE,
        related_name="api_keys",
    )

If you need to customize the model's Meta, it should inherit from AbstractAPIKey.Meta:

class OrganizationAPIKey(AbstractAPIKey):
    # ...
    class Meta(AbstractAPIKey.Meta):
        verbose_name = "Organization API key"
        verbose_name_plural = "Organization API keys"

Migrations

Because AbstractAPIKey is an abstract model, the custom API key model will have its own table in the database.

This means that you need to generate a migration and then apply it to be able to query the new API key model:

python manage.py makemigrations
python manage.py migrate

Important

If AbstractAPIKey changes (e.g. because of an update to Django REST Framework API Key), you will need to generate and apply migrations again to account for these changes.

Managers

The APIKey model as well as custom API keys models inherited from AbstractAPIKey have a dedicated manager which is responsible for implementing .create_key() and other important behavior.

As a result, if you want to build a custom API key manager, it should inherit from BaseAPIKeyManager instead of Django's Manager.

Besides customization APIs that come with Django's managers, BaseAPIKeyManager gives you one extra hook: you can override .get_usable_keys() to customize which set of API keys clients can use in authorized requests.

For example, here's how to restrict usable keys to those of active organizations only:

class OrganizationAPIKeyManager(BaseAPIKeyManager):
    def get_usable_keys(self):
        return super().get_usable_keys().filter(organization__active=True)

Check

Note the call to the parent implementation using super() here. This is because .get_usable_keys() has some default behavior, including making sure that revoked API keys cannot be used.

Tip

You don't need to use a custom model to use a custom manager — it can be used on the built-in APIKey model as well.

Admin panel

If you'd like to view and manage your custom API key model via the Django admin site, you can create and register a subclass of APIKeyModelAdmin:

# organizations/admin.py
from django.contrib import admin
from rest_framework_api_key.admin import APIKeyModelAdmin
from .models import OrganizationAPIKey

@admin.register(OrganizationAPIKey)
class OrganizationAPIKeyModelAdmin(APIKeyModelAdmin):
    pass

You can also customize any of the default attributes defined in APIKeyModelAdmin. For example, to display the organization's name in the list view, and allow searching OrganizationAPIKey instances by organization name while keeping the original search behavior, you can write:

    list_display = [*APIKeyModelAdmin.list_display, "organization__name"]
    search_fields = [*APIKeyModelAdmin.search_fields, "organization__name"]

How can I display API keys on the detail page of a related model instance?

In theory, this could be done using Django's InlineModelAdmin.

However, due to the limitations of inlines, this cannot be easily achieved while correctly saving and displaying the generated key in the detail page of the related model.

As an alternative, you can use the .list_filter class attribute to filter API keys by an identifying field on the related model. In the examples above, you could use organization__name to filter API keys by organization.

Permission classes

The built-in HasAPIKey permission class only checks against the built-in APIKey model. This means that if you use a custom API key model, you need to create a custom permission class for your application to validate API keys against it.

You can do so by subclassing BaseHasAPIKey and specifying the .model class attribute:

# organizations/permissions.py
from rest_framework_api_key.permissions import BaseHasAPIKey
from .models import OrganizationAPIKey

class HasOrganizationAPIKey(BaseHasAPIKey):
    model = OrganizationAPIKey

You can then use HasOrganizationAPIKey as described in Setting permissions.

Tip

If you need to customize .has_permission() or .has_object_permission(), feel free to read the source code.

API key parsing

By default, API key permission classes retrieve the API key from the Authorization header or a custom header, as described in Making authorized requests.

You can customize of override this behavior in several ways.

If you are building an API for an application you do not control that requires a specific header keyword, e.g. a client that sends API keys using the Bearer keyword as follows:

Authorization: Bearer <API_KEY>

Then you can subclass KeyParser with a custom keyword, and attach it to a custom permission class, like so:

# settings.py
from rest_framework_api_key.models import HasAPIKey
from rest_framework_api_key.permissions import BaseHasAPIKey, KeyParser

class BearerKeyParser(KeyParser):
    keyword = "Bearer"

class HasAPIKey(BaseHasAPIKey):
    model = APIKey  # Or a custom model
    key_parser = BearerKeyParser()

You can also override the default header-based parsing completely.

To do so, redefine the .get_key() method on your custom permission class. This method accepts the HttpRequest object as unique argument and should return the API key as an str if one was found, or None otherwise.

For example, here's how you could retrieve the API key from a cookie:

class HasAPIKey(BaseHasAPIKey):
    model = APIKey  # Or a custom model

    def get_key(self, request):
        return request.COOKIES.get("api_key")

If your custom key parsing algorithm is more complex, you may want to define it as a separate component. To do so, build a key parser class, which must implement the .get() method with the same signature as .get_key(), then set it as the .key_parser, as follows:

class CookieKeyParser:
    def get(self, request):
        cookie_name = getattr(settings, "API_KEY_COOKIE_NAME", "api_key")
        return request.COOKIES.get(cookie_name)

class HasAPIKey(BaseHasAPIKey):
    model = APIKey  # Or a custom model
    key_parser = CookieKeyParser()

Key generation

Warning

This is an advanced topic. Customizing the key generation algorithm must be done with care to prevent security issues.

If you proceed, it is best to customize key generation with a clean database state, that is before running initial migrations, and more importantly before any API key is created.

This package ships with a key generation algorithm based on Django's password hashing infrastructure (see also Security).

The .key_generator attribute on BaseAPIKeyManager allows you to customize key generation.

For example, you can customize the length of the prefix and secret key using:

from rest_framework_api_key.models import BaseAPIKeyManager
from rest_framework_api_key.crypto import KeyGenerator

class OrganizationAPIKeyManager(BaseAPIKeyManager):
    key_generator = KeyGenerator(prefix_length=8, secret_key_length=32)  # Default values

class OrganizationAPIKey(AbstractAPIKey):
    objects = OrganizationAPIKeyManager()
    # ...

If you want to replace the key generation algorithm entirely, you can create your own KeyGenerator class. It must implement the .generate() and .verify() methods. At this point, it's probably best to read the source code for the built-in KeyGenerator.

Check

If the signature of your .generate() method is different from the built-in one, you'll need to override .assign_key() in your custom API key manager as well.

Likewise, if .verify() must accept anything else than the key and hashed_key, you'll need to override .is_valid() on your custom API key model.

See models.py for the source code of BaseAPIKeyManager.

Typing support

This package provides type information starting with version 2.0, making it suitable for usage with type checkers such as mypy.

For the best experience, you may want to install packages such as django-stubs and djangorestframework-stubs. Note however that a seamless integration with these packages is not guaranteed yet.