Ninja JWT

repository·master·Indexed 42 days ago

https://github.com/eadwincode/django-ninja-jwt

A JSON Web Token (JWT) plugin for Django-Ninja, forked from Simple JWT to remove Django REST Framework dependencies. It provides JWT authentication for both class-based controllers and function-based routers, including support for asynchronous routes via AsyncJWTAuth, token blacklisting, and custom token claim configuration via schema swapping.

Tokens
7.5K
Snippets
24
Records
33
Agent score
53%

What's inside Ninja JWT

  1. Introduction to Ninja JWT

    master
    Ninja JWT is a plugin for Django Ninja that provides JSON Web Token (JWT) authentication. It is designed to handle common JWT use cases with a robust authentication backend and a practical set of default features. The library is highly extensible, allowing developers to implement custom authentication logic or features.
  2. Understand Ninja JWT token types

    master

    Ninja JWT uses a token_type claim (customizable via TOKEN_TYPE_CLAIM) in the token payload to distinguish between different authentication tokens.

    Supported types include:

    • access: The default token type used for authentication.
    • sliding: A token that contains both an expiration claim and a refresh expiration claim.
    • refresh: Used for obtaining new tokens, but is not considered valid for direct authentication.

    By default, the library expects an access token. You can control which token types are valid for authentication by configuring the AUTH_TOKEN_CLASSES setting with dot paths to the appropriate token classes.

  3. Blacklist a token via Django Admin

    master

    The blacklist app provides two models with defined admins: OutstandingToken and BlacklistedToken.

    To blacklist a token manually via the admin interface:

    1. Locate the corresponding OutstandingToken record in the Django admin.
    2. Create a new BlacklistedToken record that points to that OutstandingToken record.
  4. Implement custom JWT authentication

    master

    To create a custom authentication mechanism that still utilizes JWT logic, inherit from JWTBaseAuthentication. This is useful if you want to extract the token from a non-standard location, such as a specific API key header, while still performing JWT validation via self.jwt_authenticate(request, token=key).

    from ninja.security import APIKeyHeader
    from ninja_jwt.authentication import JWTBaseAuthentication
    from ninja import router
    
    class ApiKey(APIKeyHeader, JWTBaseAuthentication):
        param_name = "X-API-Key"
    
        def authenticate(self, request, key):
            return self.jwt_authenticate(request, token=key)
    
    
    header_key = ApiKey()
    router = router('')
    
    @router.get("/headerkey", auth=header_key)
    def apikey(request):
        return f"Token = {request.auth}"
  5. Use Sliding tokens for authentication

    master

    Sliding tokens provide a more convenient user experience by combining expiration and refresh capabilities. A sliding token remains valid for authentication as long as its expiration claim has not passed. It can also be submitted to a refresh view to receive a new token with a renewed expiration claim.

    To implement sliding tokens, follow these two steps:

    1. Update AUTH_TOKEN_CLASSES in your settings to include 'ninja_jwt.tokens.SlidingToken'.
    2. Register the NinjaJWTSlidingController to your Ninja API.

    Note on Performance: If you are using the blacklist app, Ninja JWT will validate every sliding token against the blacklist for every authenticated request, which may reduce performance.

    # 1. Update settings.py
    AUTH_TOKEN_CLASSES = ('ninja_jwt.tokens.SlidingToken',)
    
    # 2. Register the controller in your API setup
    from ninja_jwt.controller import NinjaJWTSlidingController
    from ninja_extra import NinjaExtraAPI
    
    api = NinjaExtraAPI()
    api.register_controllers(NinjaJWTSlidingController)
  6. Use Asynchronous Route Authentication

    master

    For asynchronous endpoints, use the AsyncJWTAuth class.

    Requirements:

    • The endpoint function must be defined with async def.
    • Asynchronous features require Django version > 3.0.
    from ninja_extra import api_controller, route
    from ninja_jwt.authentication import AsyncJWTAuth
    
    @api_controller
    class MyController:
        @route.get('/some-endpoint', auth=AsyncJWTAuth())
        async def some_endpoint(self):
            ...
  7. Enable the Ninja JWT token blacklist app

    master

    To enable token blacklisting functionality, add 'ninja_jwt.token_blacklist' to your INSTALLED_APPS in your Django settings.py and run migrations.

    When enabled, Ninja JWT automatically tracks generated refresh or sliding tokens in an 'outstanding tokens' list and validates that refresh/sliding tokens are not present in the blacklist before considering them valid.

    # Django project settings.py
    
    INSTALLED_APPS = (
        ...
        'ninja_jwt.token_blacklist',
        ...
    )
  8. Manually create tokens for a user

    master

    You can manually generate new refresh and access tokens for a specific user by using the RefreshToken.for_user(user) method. This is useful when you need to issue tokens outside of the standard authentication flow (e.g., during a custom login process or user registration).

    To get the usable string representations of the tokens, you must cast the RefreshToken instance and its access_token property to str. This pattern works for any subclass of ninja_jwt.tokens.Token.

    from ninja_jwt.tokens import RefreshToken
    
    def get_tokens_for_user(user):
        # Create a new RefreshToken instance for the user
        refresh = RefreshToken.for_user(user)
    
        return {
            'refresh': str(refresh),
            'access': str(refresh.access_token),
        }
  9. Customize token claims via Controller Schema Swapping

    master

    Starting from v5.2.4, you can customize the claims included in web tokens without overriding controller functions. This is achieved by creating a custom input schema and registering it in the NINJA_JWT settings.

    Requirements

    1. Your custom input schema must inherit from ninja_jwt.schema.TokenObtainInputSchemaBase or ninja_jwt.schema.TokenInputSchemaMixin.
    2. You must override get_token(cls, user) to define the dictionary of data (claims) to be returned.
    3. You must override get_response_schema() to return the schema used for the API response.

    Implementation Steps

    1. Define your schemas in a module (e.g., project/schema.py):
    from typing import Type, Dict
    from ninja_jwt.schema import TokenObtainInputSchemaBase
    from ninja.schema import Schema
    from ninja_jwt.tokens import RefreshToken
    
    class UserSchema(Schema):
        first_name: str
        email: str
    
    class MyTokenObtainPairOutSchema(Schema):
        refresh: str
        access: str
        user: UserSchema
    
    class MyTokenObtainPairInputSchema(TokenObtainInputSchemaBase):
        @classmethod
        def get_response_schema(cls) -> Type[Schema]:
            return MyTokenObtainPairOutSchema
    
        @classmethod
        def get_token(cls, user) -> Dict:
            values = {}
            refresh = RefreshToken.for_user(user)
            values["refresh"] = str(refresh)
            values["access"] = str(refresh.access_token)
            values.update(user=UserSchema.from_orm(user))
            return values
    1. Register the schema in your Django settings.py:
    NINJA_JWT = {
        'TOKEN_OBTAIN_PAIR_INPUT_SCHEMA': 'project.schema.MyTokenObtainPairInputSchema',
    }
    # project/schema.py
    from typing import Type, Dict
    from ninja_jwt.schema import TokenObtainInputSchemaBase
    from ninja import Schema
    from ninja_jwt.tokens import RefreshToken
    
    class UserSchema(Schema):
        first_name: str
        email: str
    
    class MyTokenObtainPairOutSchema(Schema):
        refresh: str
        access: str
        user: UserSchema
    
    class MyTokenObtainPairInputSchema(TokenObtainInputSchemaBase):
        @classmethod
        def get_response_schema(cls) -> Type[Schema]:
            return MyTokenObtainPairOutSchema
    
        @classmethod
        def get_token(cls, user) -> Dict:
            values = {}
            refresh = RefreshToken.for_user(user)
            values["refresh"] = str(refresh)
            values["access"] = str(refresh.access_token)
            values.update(user=UserSchema.from_orm(user))
            return values
  10. Authenticate routes using Function-Based Routers

    master

    When using standard Django Ninja routers, protect endpoints by passing an instance of JWTAuth() to the auth parameter of the router method (e.g., @router.get). The authenticated user will be available in request.user or request.auth.

    from ninja import router
    from ninja_jwt.authentication import JWTAuth
    
    router = router('')
    
    @router.get('/some-endpoint', auth=JWTAuth())
    def some_endpoint(request):
        ...