Source code for litestar.middleware.authentication

from __future__ import annotations

from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import TYPE_CHECKING, Any

from litestar.connection import ASGIConnection
from litestar.enums import HttpMethod, ScopeType
from litestar.middleware._utils import (
    build_exclude_path_pattern,
    should_bypass_middleware,
)

__all__ = ("AbstractAuthenticationMiddleware", "AuthenticationResult")


if TYPE_CHECKING:
    from collections.abc import Sequence

    from litestar.types import ASGIApp, Method, Receive, Scope, Scopes, Send


[docs] @dataclass class AuthenticationResult: """Dataclass for authentication result.""" __slots__ = ("auth", "user") user: Any """The user model, this can be any value corresponding to a user of the API.""" auth: Any """The auth value, this can for example be a JWT token."""
[docs] class AbstractAuthenticationMiddleware(ABC): """Abstract AuthenticationMiddleware that allows users to create their own AuthenticationMiddleware by extending it and overriding :meth:`AbstractAuthenticationMiddleware.authenticate_request`. """ __slots__ = ( "app", "exclude", "exclude_http_methods", "exclude_opt_key", "scopes", )
[docs] def __init__( self, app: ASGIApp, exclude: str | list[str] | None = None, exclude_from_auth_key: str = "exclude_from_auth", exclude_http_methods: Sequence[Method] | None = None, scopes: Scopes | None = None, ) -> None: """Initialize ``AbstractAuthenticationMiddleware``. Args: app: An ASGIApp, this value is the next ASGI handler to call in the middleware stack. exclude: A pattern or list of patterns to skip in the authentication middleware. exclude_from_auth_key: An identifier to use on routes to disable authentication for a particular route. exclude_http_methods: A sequence of http methods that do not require authentication. scopes: ASGI scopes processed by the authentication middleware. """ self.app = app self.exclude = build_exclude_path_pattern(exclude=exclude, middleware_cls=type(self)) self.exclude_http_methods = (HttpMethod.OPTIONS,) if exclude_http_methods is None else exclude_http_methods self.exclude_opt_key = exclude_from_auth_key self.scopes = scopes or {ScopeType.HTTP, ScopeType.WEBSOCKET}
[docs] async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: """ASGI callable. Args: scope: The ASGI connection scope. receive: The ASGI receive function. send: The ASGI send function. Returns: None """ if not should_bypass_middleware( exclude_http_methods=self.exclude_http_methods, exclude_opt_key=self.exclude_opt_key, exclude_path_pattern=self.exclude, scope=scope, scopes=self.scopes, ): auth_result = await self.authenticate_request(ASGIConnection(scope)) scope["user"] = auth_result.user scope["auth"] = auth_result.auth await self.app(scope, receive, send)
[docs] @abstractmethod async def authenticate_request(self, connection: ASGIConnection) -> AuthenticationResult: """Receive the http connection and return an :class:`AuthenticationResult`. Notes: - This method must be overridden by subclasses. Args: connection: An :class:`ASGIConnection <litestar.connection.ASGIConnection>` instance. Raises: NotAuthorizedException | PermissionDeniedException: if authentication fails. Returns: An instance of :class:`AuthenticationResult <litestar.middleware.authentication.AuthenticationResult>`. """ raise NotImplementedError("authenticate_request must be overridden by subclasses")