Skip to main content

Simple JWT-based authentication using Django and Django-ninja

Project description

django-ninja-simple-jwt

Provides simple JWT based stateless authentication for Django-ninja

Quick start

Install package:

pip install django-ninja-simple-jwt

add ninja_simple_jwt to list of INSTALLED_APPS in Django settings:

# settings.py

INSTALLED_APPS = [
    ...,
    "ninja_simple_jwt"
]

(Optional) Configure authentication mode. By default, HttpJwtAuth uses stateless authentication:

# settings.py

NINJA_SIMPLE_JWT = {
    "USE_STATELESS_AUTH": True,  # Default: True - creates TokenUser from JWT claims
    # Set to False to fetch users from database instead
}

Expose Django-ninja's API and add ninja_simple_jwt's auth API endpoint router to the API, ie:

# urls.py

from ninja import NinjaAPI
from ninja_simple_jwt.auth.views.api import mobile_auth_router, web_auth_router
from django.urls import path

api = NinjaAPI()
api.add_router("/auth/mobile/", mobile_auth_router)
api.add_router("/auth/web/", web_auth_router)

urlpatterns = [path("api/", api.urls)]

This would provide 4 available auth API endpoints for mobile and web sign in and token refresh:

  • {{server_url}}/api/auth/mobile/sign-in
  • {{server_url}}/api/auth/mobile/token-refresh
  • {{server_url}}/api/auth/web/sign-in
  • {{server_url}}/api/auth/web/token-refresh

If you are not exposing the API and routers at the exact path as the example above, see WEB_REFRESH_COOKIE_PATH setting regarding web auth token refresh path.

To protect a resource API, you can use the HttpJwtAuth as the auth argument when instantiating a Router, ie:

# views.py

from ninja_simple_jwt.auth.ninja_auth import HttpJwtAuth
from ninja import Router

some_resource_router = Router(auth=HttpJwtAuth())

@some_resource_router.get("/hello")
def hello(request):
    return "Hello world"

Finally, before starting up the server, create a key pair to be used by the server for signing and verifying JWT:

python manage.py make_rsa

You should see two files created in the root of project repository:

  • jwt-signing.pem # this is the private key used to sign a JWT, keep this secret, store appropriately
  • jwt-signing.pub # this is the public key used to verify a JWT

Documentation

Stateless authentication

By default, HttpJwtAuth uses stateless authentication, creating a TokenUser instance directly from JWT token claims without requiring database lookups. This provides better performance and scalability.

To use stateless authentication (default):

# settings.py
NINJA_SIMPLE_JWT = {
    "USE_STATELESS_AUTH": True,  # Default
}

To use database-backed authentication (fetches user from database):

# settings.py
NINJA_SIMPLE_JWT = {
    "USE_STATELESS_AUTH": False,
}

Customizing JWT key storage

By default, the management command make_rsa will create and store the JWT key pairs in the root of your project directory, this is only intended for development.

Here is an example how you can store the keys in a S3 bucket somewhere only your application has access to, assuming you are using django-storages to access AWS S3.

# some_project_dir/my_jwt_key_storage.py

from storages.backends.s3boto3 import S3Boto3Storage

aws_s3_private_key_storage = S3Boto3Storage(bucket_name="MySecretJwtKeysStorage")
aws_s3_public_key_storage = S3Boto3Storage(bucket_name="MyPublicJwtKeysStorage")

and provide the above storage instances in Django settings:

# settings.py

NINJA_SIMPLE_JWT = {
    "JWT_PRIVATE_KEY_STORAGE": "some_project_dir.my_jwt_key_storage.aws_s3_private_key_storage",
    "JWT_PUBLIC_KEY_STORAGE": "some_project_dir.my_jwt_key_storage.aws_s3_public_key_storage",
    ...
}

You can provide any custom storage implementation in this setting provided that they follow Django's Storage API.

You should make sure that the private key storage is only accessible by the auth service application. The public key storage may be made accessible by other services that need to verify the JWT issued by the auth service.

Enabling auth API endpoints

Mobile

You can enable the mobile auth end points by adding a provided router to the ninja API class:

# urls.py

from ninja import NinjaAPI
from ninja_simple_jwt.auth.views.api import mobile_auth_router
from django.urls import path

api = NinjaAPI()
api.add_router("/auth/mobile/", mobile_auth_router)


urlpatterns = [path("api/", api.urls)]

In the above example with the mobile_auth_router, you would gain the following endpoints:

  • /api/auth/mobile/sign-in
curl --location 'http://127.0.0.1:8000/api/auth/mobile/sign-in' \
--header 'Content-Type: application/json' \
--data '{
    "username": "my-username",
    "password": "my-password"
}'

The response would contain refresh and access JWT in the body:

{
  "refresh": "...",
  "access": "..."
}
  • /api/auth/mobile/token-refresh
curl --location 'http://127.0.0.1:8000/api/auth/mobile/token-refresh' \
--header 'Content-Type: application/json' \
--data '{
    "refresh": "..."
}'

The response would contain an access JWT in the body:

{
  "access": "..."
}

Web

See also: web auth endpoint design.

Similarly to the Mobile auth end point example above, you can enable the web auth endpoints by adding it to the ninja API class:

# urls.py

from ninja import NinjaAPI
from ninja_simple_jwt.auth.views.api import web_auth_router
from django.urls import path

api = NinjaAPI()
api.add_router("/auth/web/", web_auth_router)

urlpatterns = [path("api/", api.urls)]

You would gain the following endpoints:

  • /api/auth/web/sign-in
curl --location 'http://127.0.0.1:8000/api/auth/web/sign-in' \
--header 'Content-Type: application/json' \
--data '{
    "username": "my-username",
    "password": "my-password"
}'

The response would contain the access JWT in the body, however the refresh JWT is only in a cookie:

refresh=...; expires=Fri, 09 Feb 2024 03:49:33 GMT; HttpOnly; Max-Age=2591999; Path=/api/auth/web/token-refresh; SameSite=Strict

By default, this refresh cookie is only used when calling the token refresh endpoint (below).

  • /api/auth/web/token-refresh
curl --location --request POST 'http://127.0.0.1:8000/api/auth/web/token-refresh' \
--header 'Cookie: refresh=...'

The response would contain an access token in the body.

  • /api/auth/web/sign-out
curl --location --request POST 'http://127.0.0.1:8000/api/auth/web/sign-out' \

This will respond with a 204 status code and clear the refresh cookie from client. Note that this does not invalidate the token, it only removes the refresh token from the client.

Customizing token claims for user

You can specify a claim on the JWT and what User model attribute to get the claim value from using the setting TOKEN_CLAIM_USER_ATTRIBUTE_MAP. By default, this setting has the following value:

{
    # claim: model attribute
    "user_id": "id",
    "username": "username",
    "last_login": "last_login",
}

The mapping can also take a function as the value, ie:

{
    "full_name": lambda user: user.first_name + " " + user.last_name,
}

Serializing user attribute into JWT claim

If the model attribute is not by default serializeable, you can specify how to serialize it by providing a custom implementation of json encoder class. Ie:

# some_directory/custom_encoders.py

from ninja_simple_jwt.jwt.json_encode import TokenUserEncoder

class CustomTokenUserEncoder(TokenUserEncoder):
    def default(self, o: Any) -> Any:
        if isinstance(o, SomeCustomClass):
            return self.serialize_some_custom_class(o)

        return super().default(o)

    def serialize_some_custom_class(self, o: SomeCustomClass) -> str:
        # custom serialization implementation here
        return "serialized value"

And then provide the import string for this class in Django setting:

# settings.py

NINJA_SIMPLE_JWT = {
    ...,
    "TOKEN_USER_ENCODER_CLS": "some_directory.custom_encoders.CustomTokenUserEncoder"
}

Settings

All settings specific for this library are stored as key-value pairs under Django setting NINJA_SIMPLE_JWT, ie:

# settings.py
NINJA_SIMPLE_JWT = {
    ...
}

See list of all available settings.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_ninja_simple_jwt-0.7.1.tar.gz (16.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_ninja_simple_jwt-0.7.1-py3-none-any.whl (24.7 kB view details)

Uploaded Python 3

File details

Details for the file django_ninja_simple_jwt-0.7.1.tar.gz.

File metadata

  • Download URL: django_ninja_simple_jwt-0.7.1.tar.gz
  • Upload date:
  • Size: 16.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.9

File hashes

Hashes for django_ninja_simple_jwt-0.7.1.tar.gz
Algorithm Hash digest
SHA256 1243a1eb646d34bd631c0fdada76d63ece96ed4dc1dedd74297518e08f8e3910
MD5 1e7f089a26778273dbf7f4c4246a633b
BLAKE2b-256 67019d7d5e1363f84ea4929d1ed3e8fc2db263553a0eb000b74394c3c40a1a6e

See more details on using hashes here.

File details

Details for the file django_ninja_simple_jwt-0.7.1-py3-none-any.whl.

File metadata

File hashes

Hashes for django_ninja_simple_jwt-0.7.1-py3-none-any.whl
Algorithm Hash digest
SHA256 b64c960b88a09821697e4ff109886ab30c8b73f53f0fafe3cea5387d5bb870a0
MD5 17f536b950beec8546f95b25cd8473d7
BLAKE2b-256 062fe4f0b2e0bbe9214520b667d8e69fabc83cdea63adf030680d1ccd0ce3919

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page