Skip to main content

A JWT auth toolkit for Starlite

Project description

Starlite JWT

Starlite logo

PyPI - License PyPI - Python Version

Quality Gate Status Coverage

Maintainability Rating Security Rating Reliability Rating Code Smells

Discord Matrix

This library offers simple JWT authentication for Starlite.

Checkout the docs 📚.

Installation

pip install starlite-jwt

This library uses the excellent python-jose library, which supports multiple cryptographic backends. You can install either pyca/cryptography or pycryptodome, and it will be used as the backend automatically. Note that if you want to use a certificate based encryption scheme, you must install one of these backends - please refer to the python-jose readme for more details.

Example

import os
from typing import Any, Optional
from uuid import UUID, uuid4

from pydantic import BaseModel, EmailStr
from starlite import OpenAPIConfig, Request, Response, ASGIConnection, Starlite, get

from starlite_jwt import JWTAuth, Token


# Let's assume we have a User model that is a pydantic model.
# This though is not required - we need some sort of user class -
# but it can be any arbitrary value, e.g. an SQLAlchemy model, a representation of a MongoDB  etc.
class User(BaseModel):
    id: UUID
    name: str
    email: EmailStr


# The JWTAuth package requires a handler callable that takes a unique identifier, and returns the 'User'
# instance correlating to it.
#
# The identifier is the 'sub' key of the JWT, and it usually correlates to a user ID.
# It can be though any arbitrary value you decide upon - as long as the handler function provided
# can receive this value and return the model instance for it.
#
# Note: The callable can be either sync or async - both will work.
async def retrieve_user_handler(
    unique_identifier: str, connection: ASGIConnection[Any, Any, Any]
) -> Optional[User]:
    # logic here to retrieve the user instance
    ...


# The minimal configuration required for the library is the callable for the 'retrieve_user_handler' key, and a string
# value for the token secret.
#
# Important: secrets should never be hardcoded. Its best practice to pass the secret using ENV.
#
# Tip: It's also a good idea to use the pydantic settings management functionality
jwt_auth = JWTAuth(
    retrieve_user_handler=retrieve_user_handler,
    token_secret=os.environ.get("JWT_SECRET", "abcd123"),
    # we are specifying which endpoints should be excluded from authentication. In this case the login endpoint
    # and our openAPI docs.
    exclude=["/login", "/schema"],
)


# Given an instance of 'JWTAuth' we can create a login handler function:
@get("/login")
def login_handler() -> Response[User]:
    # we have a user instance - probably by retrieving it from persistence using another lib.
    # what's important for our purposes is to have an identifier:
    user = User(name="Moishe Zuchmir", email="zuchmir@moishe.com", id=uuid4())

    response = jwt_auth.login(identifier=str(user.id), response_body=user)

    # you can do whatever you want to update the response instance here
    # e.g. response.set_cookie(...)

    return response


# We also have some other routes, for example:
@get("/some-path")
def some_route_handler(request: Request[User, Token]) -> Any:
    # request.user is set to the instance of user returned by the middleware
    assert isinstance(request.user, User)
    # request.auth is the instance of 'starlite_jwt.Token' created from the data encoded in the auth header
    assert isinstance(request.auth, Token)
    # do stuff ...


# We add the jwt security schema to the OpenAPI config.
openapi_config = OpenAPIConfig(
    title="My API",
    version="1.0.0",
    components=[jwt_auth.openapi_components],
    security=[jwt_auth.security_requirement],
)

# We initialize the app instance, passing to it the 'jwt_auth.middleware' and the 'openapi_config'.
app = Starlite(
    route_handlers=[login_handler, some_route_handler],
    middleware=[jwt_auth.middleware],
    openapi_config=openapi_config,
)

Customization

This integrates with the OpenAPI configuration of Starlite, and it uses the SecurityScheme configuration to format the header and/or cookie value.

The default implementation follows the Bearer {encoded_token} format, but you may optionally override this configuration by modifying the openapi_component attribute of your JWTAuth instance.

If you wanted your authentication header to be Token {encoded_token}, you could use the following as your security scheme configuration:

from pydantic_openapi_schema.v3_1_0 import Components, SecurityScheme
from starlite_jwt import JWTAuth


class CustomJWTAuth(JWTAuth):
    @property
    def openapi_components(self) -> Components:
        """Creates OpenAPI documentation for the JWT auth schema used.

        Returns:
            An [Components][pydantic_schema_pydantic.v3_1_0.components.Components] instance.
        """
        return Components(
            securitySchemes={
                self.openapi_security_scheme_name: SecurityScheme(
                    type="http",
                    scheme="Token",
                    name=self.auth_header,
                    bearerFormat="JWT",
                    description="JWT api-key authentication and authorization.",
                )
            }
        )

Contributing

Starlite and all its official libraries is open to contributions big and small.

You can always join our discord server or join our Matrix space to discuss contributions and project maintenance. For guidelines on how to contribute to this library, please see the contribution guide.

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

starlite_jwt-1.5.0.tar.gz (14.2 kB view hashes)

Uploaded Source

Built Distribution

starlite_jwt-1.5.0-py3-none-any.whl (12.6 kB view hashes)

Uploaded Python 3

Supported by

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