Middleware

The middleware builds upon Starlette’s AuthenticationMiddleware (see the docs).

TokenAuthBackend is used to extract the token from the request. If the token is present and correct, then the request is accepted and the corresponding user is added to the scope, otherwise it is rejected.

TokenAuthBackend can work with several different TokenAuthProvider subclasses. The following are provided by default, but custom ones can be written by creating your own TokenAuthProvider subclasses.

SecretTokenAuthProvider

This provider checks whether the token provided by the client matches a list of predefined tokens.

from starlette.middleware.authentication import AuthenticationMiddleware

from piccolo_api.token_auth.middleware import (
    TokenAuthBackend,
    SecretTokenAuthProvider,
)

app = AuthenticationMiddleware(
    my_asgi_app,
    backend=TokenAuthBackend(SecretTokenAuthProvider(tokens=["abc123"])),
)

If successful a user called secret_token_user is added to the scope.

This provider is useful for protecting internal services, where the client is trusted not to leak the tokens.

It is also useful for protecting login endpoints when accessed from native apps. The client provides the token to be able to access the login endpoint, after which they obtain a unique token, which is used to authenticate with other endpoints.

PiccoloTokenAuthProvider

This provider checks a Piccolo database table for a corresponding token, and retrieves a matching user ID. It is the default provider.

from starlette.middleware.authentication import AuthenticationMiddleware

from piccolo_api.token_auth.middleware import (
    TokenAuthBackend,
    PiccoloTokenAuthProvider,
)

app = AuthenticationMiddleware(
    my_asgi_app,
    backend=TokenAuthBackend(PiccoloTokenAuthProvider()),
)

You’ll have to run the migrations for this to work correctly.


TokenAuthBackend

excluded_paths

By default, the middleware protects all of the endpoints it is wrapping (i.e. if a token isn’t present in the header then the request is rejected).

However, we may want to exclude certain endpoints - for example, if there’s a Swagger docs endpoint, and allow access to them without a token. This is possible using excluded_paths.

Paths can be specified explicitly, or using wildcards:

TokenAuthBackend(
    PiccoloTokenAuthProvider(),
    excluded_paths=["/docs", "/openapi.json", "/foo/*"],
)

Note

In the above example /foo/* matches /foo/, /foo/a, /foo/b, /foo/b/1 etc.

This is useful when using Swagger docs as they can be viewed in a browser, but they are still token protected.

FastAPI example

If we want to communicate with the API endpoints via the Swagger docs, we need to set FastAPI APIKeyHeader as a dependency. After that we can authorise the user with a valid token as in the example below.

"""
An example usage of excluded_paths.
"""

from fastapi import Depends, FastAPI
from fastapi.middleware import Middleware
from fastapi.security.api_key import APIKeyHeader
from starlette.middleware.authentication import AuthenticationMiddleware
from tables import Movie  # An example Table

from piccolo_api.crud.endpoints import PiccoloCRUD
from piccolo_api.fastapi.endpoints import FastAPIKwargs, FastAPIWrapper
from piccolo_api.token_auth.middleware import (
    SecretTokenAuthProvider,
    TokenAuthBackend,
)

app = FastAPI(
    dependencies=[Depends(APIKeyHeader(name="Authorization"))],
    middleware=[
        Middleware(
            AuthenticationMiddleware,
            backend=TokenAuthBackend(
                SecretTokenAuthProvider(tokens=["abc123"]),
                excluded_paths=["/docs", "/openapi.json"],
            ),
        )
    ],
)


# This is a quick way of building FastAPI endpoints using Piccolo, but isn't
# required:
FastAPIWrapper(
    "/movies/",
    fastapi_app=app,
    piccolo_crud=PiccoloCRUD(Movie, read_only=False),
    fastapi_kwargs=FastAPIKwargs(
        all_routes={"tags": ["Movie"]},
    ),
)
../_images/authorize_button.png ../_images/authorize_modal.png

The user can then use the Swagger docs to interact with the API.

Note

The full source code is available on GitHub. Find the source for this documentation page, and look at the examples folder.


Source

class piccolo_api.token_auth.middleware.TokenAuthBackend(token_auth_provider: TokenAuthProvider = DEFAULT_PROVIDER, excluded_paths: Sequence[str] | None = None)[source]
Parameters:
  • token_auth_provider – Used to verify that a token is correct.

  • excluded_paths – These paths don’t require a token - useful if you want to exclude a few URLs, such as docs.

class piccolo_api.token_auth.middleware.TokenAuthProvider[source]

Subclass to create your own token provider.

class piccolo_api.token_auth.middleware.PiccoloTokenAuthProvider(auth_table: Type[BaseUser] = BaseUserTable, token_table: Type[TokenAuth] = TokenAuth)[source]

Use this when the token is stored in a Piccolo database table.

class piccolo_api.token_auth.middleware.SecretTokenAuthProvider(tokens: Sequence[str])[source]

Checks that the token belongs to a predefined list of tokens. This is useful for very simple authentication use cases - such as internal microservices, where the client is trusted.