Skip to content

Authentication in Panther#

Authentication in Panther ensures that only authorized users can access your APIs and WebSocket connections. You can configure which authentication class to use in your configs.


How Authentication Works#

  • The auth parameter for an API or WebSocket should be an async function or a class with an async __call__ method. Panther will use this callable to verify the user.
  • If you do not set auth, Panther will use the default AUTHENTICATION from your config when it is configured.
  • Built-in JWT authentication classes return None when their expected token source is absent, so request.user can be None; use permissions when an endpoint requires an authenticated user.
  • If authentication fails, Panther raises HTTP_401_UNAUTHORIZED.
  • The authenticated user is available as:
  • request.user in API views
  • self.user in WebSocket connections

Note: With built-in JWT authentication, missing credentials leave request.user as None. Use permissions such as IsAuthenticated when an endpoint must require a user.


Built-in Authentication Classes#

Panther provides three built-in authentication classes, all based on JWT (JSON Web Token):

1. JWT Authentication#

  • Retrieves the JWT token from the Authorization header in the request.
  • Expects the header format: Authorization: Bearer <token>
  • Decodes the token, validates it, and fetches the corresponding user.
  • By default, uses panther.db.models.BaseUser as the user model unless you set USER_MODEL in your configs.
  • Handles token revocation if Redis is connected (for logout and refresh scenarios).

Example usage#

AUTHENTICATION = 'panther.authentications.JWTAuthentication'

JWT Configuration#

You can customize JWT behavior by setting JWT_CONFIG in your configs. Example:

core/configs.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
from datetime import timedelta
from panther.utils import load_env  
from pathlib import Path

BASE_DIR = Path(__name__).resolve().parent  
env = load_env(BASE_DIR / '.env')

SECRET_KEY = env['SECRET_KEY']

JWT_CONFIG = {
    'key': SECRET_KEY,              # Secret key for signing tokens (default: `SECRET_KEY`)
    'algorithm': 'HS256',           # Algorithm used for JWT (default: `'HS256'`)
    'life_time': timedelta(days=2), # Access token lifetime (default: `timedelta(days=1)`)
    'refresh_life_time': timedelta(days=10), # Refresh token lifetime (default: `2 * life_time`)
}

2. QueryParam JWT Authentication#

  • Works like JWTAuthentication, but expects the token in the query parameters instead of headers.
  • Useful for WebSocket authentication or scenarios where headers are not available.
  • Pass the token as a query parameter:
  • Example: https://example.com?authorization=<jwt_token>

Example usage#

AUTHENTICATION = 'panther.authentications.QueryParamJWTAuthentication'

  • Works like JWTAuthentication, but expects the token in cookies.
  • Looks for access_token in cookies for authentication.
  • Optionally, can use refresh_token in cookies for token refresh.
  • Pass the token in cookies:
  • Example: Cookies: access_token=<jwt_token>

Example usage#

AUTHENTICATION = 'panther.authentications.CookieJWTAuthentication'

WebSocket Authentication#

For WebSocket connections, it is recommended to use QueryParamJWTAuthentication since headers are not always available. To enable this, set the following in your configs:

WS_AUTHENTICATION = 'panther.authentications.QueryParamJWTAuthentication'

Custom Authentication#

You can implement your own authentication logic by either: - Inheriting from panther.authentications.BaseAuthentication and implementing an async __call__ method (recommended for consistency), or - Providing any async function or class with an async __call__ method.

Steps to create a custom authentication class:#

  1. Inherit from BaseAuthentication and implement async __call__
    from panther.request import Request
    from panther.exceptions import AuthenticationAPIError
    from panther.authentications import BaseAuthentication
    
    class CustomAuthentication(BaseAuthentication):
        async def __call__(self, request: Request):
            # Your authentication logic here
            # Return an instance of USER_MODEL (default: BaseUser)
            # Or raise AuthenticationAPIError on failure
            ...
    
    Or as a function or plain class:
    async def custom_authentication(request: Request):
        # Your authentication logic here
        # Return an instance of USER_MODEL (default: BaseUser)
        # Or raise AuthenticationAPIError on failure
        ...
    
  2. Configure your custom authentication class or function in your configs (if you want it as default):
    AUTHENTICATION = 'project_name.core.authentications.CustomAuthentication'
    
  3. Or set it per API using the auth parameter:
    @API(auth=CustomAuthentication)
    async def my_api(request: Request):
        ...
    

Error Handling#

  • If authentication fails, raise panther.exceptions.AuthenticationAPIError with an appropriate message.
  • Panther will automatically handle and return a 401 Unauthorized response.

Summary#

  • Choose and configure the appropriate authentication class or function for your use case.
  • Use JWT configuration options to control token behavior.
  • For WebSocket, prefer query parameter-based authentication.
  • Implement custom authentication as an async function or a class with an async __call__ method.
  • With built-in JWT authentication, missing credentials leave request.user as None; use permissions to require authenticated access.