Api key and Django Rest Framework Auth Token

If I understand you correctly, then Json Web Tokens is the solution for your needs. There is a really good django package available that integrates smoothly with django rest framework: django-rest-framework-jwt.

With this package you can

  1. set the expiration time
  2. reactivate or revoke the key
  3. determine from every external call to your api, if the token is valid

Still

Hope that helps.


I have created a new authentication backend and a new token model to avoid side effect on build-in token behaviour.

models.py

class ApiKeyToken(models.Model):
    key = models.CharField(max_length=40, primary_key=True)
    company = models.ForeignKey(Company)
    is_active = models.BooleanField(default=True)

    def save(self, *args, **kwargs):
        if not self.key:
            self.key = self.generate_key()
        return super(ApiKeyToken, self).save(*args, **kwargs)

    def generate_key(self):
        return binascii.hexlify(os.urandom(20)).decode()

    def __unicode__(self):
        return self.key

authentication.py

class ApiKeyAuthentication(TokenAuthentication):

    def get_token_from_auth_header(self, auth):
        auth = auth.split()
        if not auth or auth[0].lower() != b'api-key':
            return None

        if len(auth) == 1:
            raise AuthenticationFailed('Invalid token header. No credentials provided.')
        elif len(auth) > 2:
            raise AuthenticationFailed('Invalid token header. Token string should not contain spaces.')

        try:
            return auth[1].decode()
        except UnicodeError:
            raise AuthenticationFailed('Invalid token header. Token string should not contain invalid characters.')

    def authenticate(self, request):
        auth = get_authorization_header(request)
        token = self.get_token_from_auth_header(auth)

        if not token:
            token = request.GET.get('api-key', request.POST.get('api-key', None))

        if token:
            return self.authenticate_credentials(token)

    def authenticate_credentials(self, key):
        try:
            token = ApiKeyToken.objects.get(key=key)
        except ApiKeyToken.DoesNotExist:
            raise AuthenticationFailed('Invalid Api key.')

        if not token.is_active:
            raise AuthenticationFailed('Api key inactive or deleted.')

        user = token.company.users.first()  # what ever you want here
        return (user, token)

Then you can request secured api with:

curl http://example.com/api/your-awesome-api.json -H "Authorization: Api-Key {token}" 

The djangorestframework-api-key library may be a better option currently.

From the docs:

Django REST Framework API Key is a powerful library for allowing server-side clients to safely use your API. These clients are typically third-party backends and services (i.e. machines) which do not have a user account but still need to interact with your API in a secure way.

It's a well-supported and simple-to-use way of releasing new API keys manually or programatically for Django REST Framework projects.

Simplest integration:

# settings.py

INSTALLED_APPS = [
  # ...
  "rest_framework",
  "rest_framework_api_key",
]
python manage.py migrate
# settings.py
REST_FRAMEWORK = {
    "DEFAULT_PERMISSION_CLASSES": [
        "rest_framework_api_key.permissions.HasAPIKey",
    ]
}

Then you can create new API keys through admin interface or programatically through the rest_framework_api_key.models.APIKey object.

Edit: Tokens can be revoked as well