Giter Site home page Giter Site logo

koladev32 / drf-apikey Goto Github PK

View Code? Open in Web Editor NEW
30.0 3.0 2.0 186 KB

πŸ” A simple package for API Key authentication in Django Rest with rotation integrated.

Home Page: https://djangorestframework-simple-apikey.readthedocs.io/en/latest/

License: Apache License 2.0

Python 98.75% Makefile 1.25%
api apikey-authentication django django-rest-framework fernet

drf-apikey's Introduction

Django REST Framework Simple API Key πŸ”

Django REST Framework Simple API Key is a fast and secure API Key authentication plugin for REST API built with Django Rest Framework.

For the full documentation, visit https://djangorestframework-simple-apikey.readthedocs.io/en/latest/.

Introduction

Django REST Simple Api Key is a package built upon Django, Django REST Framework, and the fernet cryptography module to generate, encrypt, and decrypt API keys. It provides fast, secure and customizable API Key authentication.

Benefits

Why should you use this package for your API Key authentication?

  • ⚑️Fast: We use the fernet cryptography module to generate, encrypt, and decrypt API keys. Besides the security facade, it is blazing fast allowing you to treat requests quickly and easily.

  • πŸ” Secure: Fernet guarantees that a message encrypted using it cannot be manipulated or read without the key, which we call FERNET_KEY. As long as you treat the fernet key at the same level you treat the Django SECRET_KEY setting, you are good to go.

  • πŸ”§ Customizable: The models, authentication backend, and permissions classes can be rewritten and fit your needs. We do our best to extend Django classes and methods, so you can easily extend our classes and methods.πŸ˜‰ Your Api Key authentication settings are kept in a single configuration dictionary named SIMPLE_API_KEY in the settings.py file of your Django project. It can be customized to fit your project needs.

Quickstart

1 - Install with pip:

pip install djangorestframework-simple-apikey

2 - Register the app in the INSTALLED_APPS in the settings.py file:

# settings.py

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

3- Add the FERNET_KEY setting in your SIMPLE_API_KEY configuration dictionary. You can easily generate a fernet key using the python manage.py generate_fernet_key command. Keep in mind that the fernet key plays a huge role in the api key authentication system.

SIMPLE_API_KEY = {
    "FERNET_SECRET": "sVjomf7FFy351xRxDeJWFJAZaE2tG3MTuUv92TLFfOA="
}

4 - Run migrations:

python manage.py migrate

In your view then, you can add the authentication class and the permission class.

⚠️ Important Note: By default, authentication is performed using the AUTH_USER_MODEL specified in the settings.py file.

from rest_framework import viewsets

from rest_framework_simple_api_key.backends import APIKeyAuthentication
from rest_framework.response import Response

class FruitViewSets(viewsets.ViewSet):
    http_method_names = ["get"]
    authentication_classes = (APIKeyAuthentication, )

    def list(self, request):
        return Response([{"detail": True}], 200 )

Generate a Fernet Key

We've made it easier for you by creating a custom Django command to quickly generate a fernet key, which is a crucial component in the authentication system. Make sure to keep the key secure and store it somewhere safely (ie: environment variable).

Important ⛔️ : You should treat the FERNET_KEY security at the same level as the Django SECRET_KEY. 🫑

To generate the fernet key use the following command:

python manage.py generate_fernet_key

Rotation

We implement an API key rotation strategy for this package. To learn more about it, refer to the documentation at https://djangorestframework-simple-apikey.readthedocs.io/en/latest/rotation.html.

Demo

You can find a demo in project in the example directory. To run the project, you can :

cd example
pip install -r requirements.txt

python manage.py migrate
python manage.py runserver

Changelog

See CHANGELOG.md.

Contributing

Thank you for your interest in contributing to the project! Here's a guide to help you get started:

  • Setup Development Environment:

    git clone https://github.com/koladev32/djangorestframework-simple-apikey.git

    Use the command below to set up your environment:

    make install
    
  • Format the Code:
    Before submitting any code, please ensure it is formatted according to our standards:

    make format
    
  • Check Code and Migrations:
    Validate your changes against our checks:

    make check
    
  • Run Migrations:
    If your changes include database migrations, run the following:

    make migrations
    
  • Run Tests:
    Always make sure your changes pass all our tests:

    make test
    

See CONTRIBUTING.md.

drf-apikey's People

Contributors

ederene20 avatar koladev32 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

Forkers

cgelb reilove

drf-apikey's Issues

Bad design for api key customization

Describe the bug

The way the model is designed sets a complicated usage for the developers mostly when we have a custom entity model. When the AUTHENTICATION_MODEL is modified, a second migration is created inside the package, if the user has run django-admin makemigrations.

Solution

For better usage, we will remove the AUTHENTICATION_MODEL setting, and add an abstract class that can be extended for custom usages.

Support Django 4.x

Hey guys,

I'm migrating my system from Django 3.2 to the version 4.2 (rc1).

I was using the lib djangorestframework-api-key in order to manage my API Keys. But it does not support Django 4+...

So I found your lib and I was wondering if it does support Django 4.1 or even more (4.2, etc) ?

Have a nice day!

Rename project

Rename projet to drf-apikey for the sake of simplicity and pronouncing.

Add creation date field on ApiKey

We need to add a field called created on the ApiKey model to track the date of creation of the API key. Will be helpful in managing the API Keys from the Django admin dashboard.

Key rotation

Key rotation is important because it enhances security by limiting the potential damage caused by a compromised key and helps ensure compliance with security standards and regulations, promoting robust keys management practices.

and because we are using Fernet which provides a key rotation feature, we can easily integrate some strategies for keys rotations.

First of all, let's define the strategies that will be used for keys rotation in this package :

  • Time-based rotation: This strategy involves rotating encryption keys at regular time intervals, such as daily, weekly, or monthly. By changing keys at fixed intervals, we ensure that each key is used for a limited period, reducing the risk of key compromise. Time-based rotation is predictable and easy to manage, as it does not depend on the specific usage patterns of the keys. This will be the default implemented strategy. By default, all keys are rotated every 6 months and the period of rotation is about three days.
  • User-based rotation: In this strategy, encryption keys are rotated based on if a setting is set to True (API_KEY_ROTATION_ENABLED). If set to True, then the rotation is made until the setting is set to False. This will be useful in case of emergencies for the developer or the team.

Implementation

The implementation will follow three phases:

  1. Add the required settings such as

    1. ROTATION_ENABLED to force simple-apikey to start a rotation. The default value is False.
    2. ROTATION_PERIOD is a timedeldta instance. By default, the rotation takes place for 3 days, thus coming with the default value of timedelata(days=3).
    3. ROTATION_DATE is the date on which the rotation should be done. We will use a crontab syntax of a custom syntax. The goal is to be able to tell simple-apikey when to start the rotation between the period of transition.
  2. Next step, we need to add a class to handle the rotation of the keys. Thus when we find ourselves in the period of transition, meaning that ROTATION_ENABLED is set to True or we are within the ROTATION_PERIOD after the ROTATION_DATE, a custom class using MultiFernet will be used instead of the conventional class for decrypting and encrypting data. When passing fernet keys to MultiFernet, the old key is used to decrypt API_KEYS while the new fernet key or ROTATION_KEY is used for encrypting new keys.

  3. Modify your custom authentication backend to conditionally use the key rotation class based on the fact that there is a rotation happening. For this we can add a function as an util, to check if a rotation is happening rotation_happening. If the function returns a result that is True, the backend should use the key rotation class for encrypting and decrypting API keys. If it's False, it should use the standard Fernet class.

  4. Update the documentation accordingly and explain the process of keys rotation and the strategies implemented. We will also add a blog to describe how to use the feature without any issues or complications.

This feature is a bit complex, and we want to ensure that the modifications are backward compatible.

Nice features to add after are the following :

  • Add a system to inform Api keys users that a key rotation is happening and that they should change their API keys. It can be done via email or notification if possible.
  • Add a feature to inform the providers of API keys (The developer using the package) about strange API keys usage. It can be uncommon for requests and much more.
  • Add a model to track rotation that has been made.

Bug: Default settings are not loaded in the project

Default settings are not loaded in the project. When there is no SIMPLE_API_KEY setting provided, Django will throw an error.

  File "/Users/koladev/projects/test-project/venv/lib/python3.11/site-packages/rest_framework_simple_api_key/models.py", line 15, in APIKeyManager
    key_crypto = ApiKeyCrypto()
                 ^^^^^^^^^^^^^^
  File "/Users/koladev/projects/test-project/venv/lib/python3.11/site-packages/rest_framework_simple_api_key/crypto.py", line 20, in __init__
    fernet_key, api_key_lifetime = settings.SIMPLE_API_KEY.get(
                                   ^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/koladev/projects/test-project/venv/lib/python3.11/site-packages/django/conf/__init__.py", line 94, in __getattr__
    val = getattr(_wrapped, name)
          ^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'Settings' object has no attribute 'SIMPLE_API_KEY'

Bug when generating a fernet key using django-admin command

Describe the bug
In the documentation, it is stated that we can create a fernet key using the following command

django-admin generate_fernet_key

To Reproduce
Steps to reproduce the behavior:

  1. Create a Django project and install the project.
  2. Run the command django-admin generate_fernet_key
  3. See the error

Expected behavior

The command should actually return a fernet key, like when we use the python manage.py generate_fernet_key command.

Screenshots
If applicable, add screenshots to help explain your problem.

image

Desktop (please complete the following information):

  • OS: MacOS Ventura 13.2 (22D49)
  • Python Version: 3.11
  • Django Version: 4.1.7
  • Package: 0.1.0

Add ApiKey model

  • Add an ApiKey model that can be extended and used with users for authentication.
  • Add adequate tests.
  • Ensure we can retrieve details about a user by decrypting and encrypting user data.
  • Add a method revoking an API key.
  • Add a method to revoke by mass.

Add Translations to the Documentation

Is your feature request related to a problem? Please describe.

As the Django REST Framework Simple API Key gains traction, we recognize that our documentation may not be accessible to all developers worldwide due to language barriers. Considering that Django and Python have a vast global user base, we're missing out on reaching a significant portion of the developer community.

Describe the solution you'd like

We propose to provide translations of our documentation into key languages to make it more inclusive and accessible. Initially, we could target the following languages, given the broad reach of Django and Python:

  • Spanish
  • French

Additionally, we can leverage platforms like Read the Docs that support multi-language documentation to facilitate this.

Describe alternatives you've considered

One alternative could be to provide only key sections of the documentation in multiple languages, instead of the entire documentation. This might be easier to maintain and update. However, this might not give non-English-speaking users the full experience.

Another alternative is to use automated translation tools or plugins, but these often lack the nuance and accuracy of human translations, especially for technical content.

Additional context

Engaging the Django and Python communities for translation contributions can serve a dual purpose: ensuring accuracy in translations and deepening community involvement. A community-driven approach can be both effective and beneficial in strengthening ties with users worldwide.

Add a parameter for retrieving the entity attribute from the request instead of `request.user`

For the moment, we are using request.user to retrieve the entity attached to the API key. For a better developer experience, we are planning to retrieve the entity from request.entity instead of request.user.
The catch here is that we should also allow the user to set the attribute on the request object. Meaning that we would do something like:

entity = getattr(request, settings.SIMPLE_API_KEY["REQUEST_ENTITY_ATTRIBUTE"], None)

To discuss surely.

Rate Limiting

Is your feature request related to a problem? Please describe.

The absence of a rate-limiting feature in the current implementation makes the API susceptible to abuse, such as brute-force attacks, and may lead to system overloads. A rate-limiting feature would prevent such issues, enhancing the API’s resilience and performance.

Describe the solution you'd like

Implement a rate-limiting feature based on the API key. This feature should allow configuration of the number of requests a user can make within a specific time frame. It should also support customization, setting different rate limits for API keys or use cases. Essential components of this feature should include error handling to return meaningful messages when a rate limit is exceeded, headers to inform users of their usage status, and logging to keep track of rate-limiting events for analysis and monitoring.

Describe alternatives you've considered

An alternative could be integrating third-party middleware for rate limiting. However, having a built-in feature would ensure better compatibility and a more streamlined configuration and usage experience.

Additional context

Here is an example of how the rate-limiting configuration could look:

SIMPLE_API_KEY = {
    "RATE_LIMIT": "100/hour",  # 100 requests per hour
    "CUSTOM_RATE_LIMITS": {
        "special_api_key": "1000/hour",  # Custom rate limit for a specific API key
    },
}

This configuration allows for a default rate limit and custom rate limits for specific API keys, enhancing flexibility and control over API access.

Can not generate a new fernet key if one is not already configured in the setting

Describe the bug
I can only run python manage.py generate_fernet_key when there is already a valid fernet key in the SIMPLE_API_KEY dictionnary config. Otherwise an error is generated.
That error is a validation from the constructor of the ApiKeyCrypto class. I haven't figured out yet exactly why it is happening.

To Reproduce
Steps to reproduce the behavior:

  1. Go to your django settings file.
  2. If you have already configured the SIMPLE_API_KEY dictionnary, change the fernet key value:
    SIMPLE_API_KEY = { "FERNET_SECRET": "", #... }

In this example it is an empty string but you can also put a random string value.
3. Now run python manage.py generate_fernet_key

Expected behavior
A Key error from ApiKeyCrypto is going to be raised.

raise KeyError("A fernet secret is not defined in the Django settings.")
KeyError: 'A fernet secret is not defined in the Django settings.'

Screenshots

Screenshot from 2023-02-26 16-50-51

Desktop:

  • OS: Linux / ElementaryOS
  • Python Version: 3.10.9
  • Django Version: 4.1.7
  • DRF Version: 3.14.0
  • Package version: 0.1.0

Additional context
My guess is that django import the classes in our apps and maybe run the constructors if any when we execute python manage.py [command] to make sure that everything thing is ok. I have tried to directly comment the if conditon which raise the error in the __init__ method of the ApiKeyCrypto class from the installed package source code and it worked fine.

Screenshot from 2023-02-26 17-12-56

DoesNotExist error not related to custom model

Describe the bug

When extending the API key model and backend, we have a 500 error on the server when there is no entity matching for the API key because of this line https://github.com/koladev32/djangorestframework-simple-apikey/blob/bc0311dbe57385445d61c2baf11813066dac4de3/rest_framework_simple_api_key/backends.py#L61.

To Reproduce

Steps to reproduce the behavior:

  1. Extend the API Key model
  2. Extend the Api Key backend with the custom API Key model
  3. Make a request with an expired API Key for example.

Expected behavior

Return an authentication error message.

Add default permissions classes

The goal is to add a default permission class that can be used in any APIView classes.

The permission class will check by default if the user is active. Then, it will throw an error accordingly or let the user continue the action requested.

Add authentication backend

We need to add an authentication backend for the project.

  • a class called ApiKeyAuthentication
  • Checks for expiration before returning the key
  • Write validation errors
  • Add a parser ApiKeyParser to retrieve api_key from the headers

Migrate to Django 4.2 LTS

Is your feature request related to a problem? Please describe.

Django has released an LTS version of the library. We will move to this version as it will be stable for a long time.

Describe the solution you'd like
Migrate the Django requirements to 4.2 LTS.

Work to do

  • Check for potential regressions and security issues
  • Check also the DRF version to ensure compatiblity

Add an exemple project to the repository

Is your feature request related to a problem? Please describe.

There is no example project for this package. It will be great to include one to showcase to developers how to effectively use the package.

Describe the solution you'd like

We will create a folder called example at the root of the project. Inside the newly created directory, we will then add a Django project using the package with clear instructions on how to install and start the project.

Add an exemple project showcasing how to use the package

Is your feature request related to a problem? Please describe.

It will help the developers see an actual implementation of the project done by the maintainers but also can serve as a quick demo.

Describe the solution you'd like
A simple Django project with an API using API authentication for controlling accessing endpoint.

Add package for linting, coverage and syntax checker

The project does not have a pipeline to check for lint errors, coverage, and syntax. The first step is to install the packages such as autoflake, flake8, flake8-bugbear, coverage, issort, and flake8-comprehensions.

Prepare package for deployment

  • Add required configuration for deployment on PyPI
  • Add GitHub action to automate the deployment of a new version of the package

Add Api Cryto methods utils

The ApiKeyCrypto is a class that will contain the methods needed to generate, encrypt and decrypt an API Key. The class will come with 3 methods:

  • generate, a method that will create the API key by using the encrypt method that encrypts data into a Fernet key.
  • encrypt, a method that will use the fernet.encrypt method to encrypt a payload.
  • decrypt, a method that will use the fernet.decrypt method to decrypt an API key.

Analytics and Monitoring

Is your feature request related to a problem? Please describe.

The current implementation lacks detailed analytics and monitoring of API key usage. Without this, gaining insights into how API keys are used, identifying patterns, troubleshooting issues, and optimizing performance is challenging. This absence of visibility can hinder effective management and the proactive resolution of potential issues.

Describe the solution you'd like

I propose the integration of an analytics and monitoring system specifically tailored to API key usage. This system should provide real-time insights such as the number of requests, most accessed endpoints, and error rates for each API key. A comprehensive dashboard that visually represents this data would be highly beneficial, allowing for easy interpretation and analysis. Additionally, maintaining historical data would facilitate trend analysis and the review of API key usage over time.

Describe alternatives you've considered

An alternative could be the integration with third-party analytics and monitoring tools. However, a built-in solution specifically designed for this package would ensure better compatibility, consistency, and ease of use. Another alternative could be manual log analysis, which would be more time-consuming, less efficient, and likely less comprehensive than an automated analytics and monitoring system.

Additional context

Having a robust analytics and monitoring system would significantly enhance the package by providing valuable insights into API key usage patterns and performance. This would facilitate better management, optimization, and troubleshooting, ultimately leading to a more secure and efficient system. The feature could also include alerting mechanisms to notify administrators of unusual activities or exceeded thresholds, enabling a quick response to potential issues.

Add a script to generate a Fernet key

If the developer has to set the Fernet key, he needs to use the shell, import the cryptography package and make calls to generate a key. We can add a utility to handle that, a Django command.

django-admin generate fernetkey

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.