moiseshiraldo / django-rest-paseto-auth

PASETO authentication for Django REST framework
MIT License
7 stars 1 forks source link

PASETO authentication for Django REST framework

PyPI version Build Status Test coverage status

Still in development, NOT READY for production.

Before using this, see https://github.com/paragonie/paseto for more information about PASETO and https://github.com/rlittlefield/pypaseto about the Python implementation.

Motivations and objectives

I needed a token authentication system for a new project and none of the available third party authentication pacakges covered my requirements completely. After some work developing my own system, I thought it would be interesting to share it and accept suggestions and contributions.

My goal is to build a token authentication system that meets the following requirements:

Installation and configuration

Install using pip:

pip install django-rest-paseto-auth

Generate a 32-bytes hexadecimal secret key:

import secrets
secrets.token_hex(32)
'55acd7321e85e62d0fe5ee6ea127ba4bd8ac90f6ea87f1bf2d3d5e816399d7d2'

Add it to your Django configuration and keep it as secured as the project's SECRET_KEY:

PASETO_KEY = '55acd7321e85e62d0fe5ee6ea127ba4bd8ac90f6ea87f1bf2d3d5e816399d7d2'

Add paseto_auth to your installed applications:

INSTALLED_APPS = (
    ...
    'paseto_auth',
)

And apply migrations:

python manage.py migrate paseto_auth

Include paseto auth URLs:

from django.urls import include, path

urlpatterns = [
    path('api/auth/', include('paseto_auth.urls', namespace='paseto_auth')),
]

Finally, add the authentication scheme to the REST_FRAMEWORK configuration or the views you want to protect:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'paseto_auth.authentication.PasetoAuthentication',
    )
}

Optional configuration with default values:

PASETO_AUTH = {
    'HEADER_PREFIX': 'Paseto',  # Prefix for the authentication header, e.g. Bearer
    'ACCESS_LIFETIME': 5*60,  # Max: 10*60 seconds
    'REFRESH_SHORT_LIFETIME': 12*3600,  # Max: 24*3600 seconds
    'REFRESH_LONG_LIFETIME': 30*24*3600,  # Max: 60*24*3600 seconds
    'REFRESH_PERMANENT_LIFETIME': 2*365*24*3600  # seconds
}

Usage

To get a token pair from user credentials:

$ curl \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"username": "testuser", "password": "qwerty", "remember": true}' \
  http://localhost:8000/api/auth/token/

----

{
  'access_token': 'v2.local.wSpANWW6wNkQoVhqCWRkUp-wPfoc6fFsml7kmNlmuccDdLpqpVKmOZy6C1cYttzIt0OM-DL2uOWQKcahje0u1uSceG5mzXBZVMjDZnbXZMamF5X5JDTCZrAruVSGZ5EtliHJTFkHkgvp8c3Xmut9_8fWI09Qn6U0gaWPgM8hM_eRi7FXNHvE7ZeGOrE37SImnVZm-jCGBgMYjWzOowzQ6ZH6JvaC07eWyh6zsGQGM-l65sBlbJtTHA',
  'refresh_token': 'v2.local.ZYSSnCB9Qc7FlABtXKq2Pl6uZ_Snd9P_iCBnxx18d1cYezN85fB40C_1YSr27lSVNdpeGX6usp8rEEnb3EHF5_B0sNfbG8HAoxqET0RDsVj9XSj5x8w-3jgHLzaHW-Zc6r9C_cY-wLRmMNL7obEq4ETwoYZTaLKcbxRH67GRCpQP1Rjil9ex9EGL6HKg26oJuxFG_hhlCzPYOMzgDDqUoQsl4AkdGq7fZzvZkBugXvVgY64s0TS2H10'
}

The remember parameter will determine the refresh token short/long lifetime (see configuration section).

To get a new access token:

$ curl \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"refresh_token": "v2.local.ZYSSnCB9Qc7FlABtXKq2Pl6uZ_Snd9P_iCBnxx18d1cYezN85fB40C_1YSr27lSVNdpeGX6usp8rEEnb3EHF5_B0sNfbG8HAoxqET0RDsVj9XSj5x8w-3jgHLzaHW-Zc6r9C_cY-wLRmMNL7obEq4ETwoYZTaLKcbxRH67GRCpQP1Rjil9ex9EGL6HKg26oJuxFG_hhlCzPYOMzgDDqUoQsl4AkdGq7fZzvZkBugXvVgY64s0TS2H10"}' \
  http://localhost:8000/api/auth/token/refresh/

----

{
  'access_token': 'v2.local.wSpANWW6wNkQoVhqCWRkUp-wPfoc6fFsml7kmNlmuccDdLpqpVKmOZy6C1cYttzIt0OM-DL2uOWQKcahje0u1uSceG5mzXBZVMjDZnbXZMamF5X5JDTCZrAruVSGZ5EtliHJTFkHkgvp8c3Xmut9_8fWI09Qn6U0gaWPgM8hM_eRi7FXNHvE7ZeGOrE37SImnVZm-jCGBgMYjWzOowzQ6ZH6JvaC07eWyh6zsGQGM-l65sBlbJtTHA',
}

App tokens

You can create user-independent refresh tokens for app integrations, with a pesudo-permanent lifetime (PAESETO_AUTH['REFRESH_PERMANENT_LIFETIME'] setting) and custom Django groups/permissions. For example, to implement something like GitHub personal API tokens, you could do:

from paseto_auth.tokens import create_app_token

obj, refresh_token = create_app_token(
   name="Custom application",
   owner=user,
   groups=groups,
   perms=permissions,
)

Where owner is a generic foreign key to any object. A reversed relation could look like:

from django.db import models
from django.contrib.contenttypes.fields import GenericRelation

class MyUserModel(models.Model):
    ...
    api_tokens = GenericRelation('paseto_auth.AppRefreshToken')

The create_app_token function returns the token object stored in the database and the refresh token string, that can be used to obtain access tokens an authenticate like a normal user. The authentication class will return an instance of AppIntegrationUser that implements all the methods from the Django PermissionsMixin.