Zum Inhalt
Überwachte maschinelle Übersetzung

Dieser Inhalt wurde maschinell übersetzt und basiert auf von Menschen kuratierten Glossaren und Styleguides. Da der Text nicht zeilenweise manuell überprüft wird, können gelegentlich Fehler oder unklare Formulierungen auftreten.

Bei etwaigen Abweichungen ist die ursprüngliche englische Version die maßgebliche Quelle.

Lesen Sie die ursprüngliche englische Version

Auth

Vollständige Referenz aller Attribute und Methoden der Auth-Provider, generiert aus deren Docstrings. Eine aufgabenorientierte Anleitung finden Sie unter Authentication.

starlette_admin.auth.base.BaseAuthProvider

Bases: ABC

Minimal contract every auth provider must satisfy.

Parameters:

Name Type Description Default
login_path str

Path for the login page/redirect.

'/login'
logout_path str

Path for the logout endpoint.

'/logout'
allow_routes Sequence[str] | None

Route names that bypass the auth check.

None
Source code in starlette_admin/auth/base.py
class BaseAuthProvider(ABC):
    """
    Minimal contract every auth provider must satisfy.

    Args:
        login_path: Path for the login page/redirect.
        logout_path: Path for the logout endpoint.
        allow_routes: Route names that bypass the auth check.
    """

    def __init__(
        self,
        login_path: str = "/login",
        logout_path: str = "/logout",
        allow_routes: Sequence[str] | None = None,
    ) -> None:
        self.login_path = login_path
        self.logout_path = logout_path
        self.allow_routes: list[str] = (
            list(allow_routes) if allow_routes is not None else []
        )
        # Set by `BaseAdmin._init_auth`, used to emit `AFTER_LOGIN`.
        self.events: AdminEventBus | None = None
        _log.debug(
            "auth provider init: %s login_path=%r logout_path=%r allow_routes=%r",
            type(self).__name__,
            login_path,
            logout_path,
            self.allow_routes,
        )

    @abstractmethod
    async def authenticate(self, request: Request) -> AdminUser | None:
        """Authenticate the current request.

        Returns:
            An `AdminUser` if the request is authenticated, or `None` to deny access.
            `AuthMiddleware` stores the result on `request.state.admin_user` and sets
            `request.state.is_anonymous` accordingly.
        """

    def get_routes(self, templates: Jinja2Templates) -> list[Route]:
        """Return the routes this provider mounts inside the admin prefix."""
        return []

    def get_middleware(self) -> Middleware:
        return Middleware(AuthMiddleware, provider=self)

    async def _emit_after_login(self, request: Request, user: AdminUser | None) -> None:
        """Emits `AFTER_LOGIN` on the admin's event bus, once login succeeds."""
        if self.events is None:
            return
        ctx = AfterLoginContext(
            event=AdminEvent.AFTER_LOGIN,
            request=request,
            view_key="",
            user=user,
        )
        await self.events.emit(ctx)

authenticate(request) abstractmethod async

Authenticate the current request.

Returns:

Type Description
AdminUser | None

An AdminUser if the request is authenticated, or None to deny access.

AdminUser | None

AuthMiddleware stores the result on request.state.admin_user and sets

AdminUser | None

request.state.is_anonymous accordingly.

Source code in starlette_admin/auth/base.py
@abstractmethod
async def authenticate(self, request: Request) -> AdminUser | None:
    """Authenticate the current request.

    Returns:
        An `AdminUser` if the request is authenticated, or `None` to deny access.
        `AuthMiddleware` stores the result on `request.state.admin_user` and sets
        `request.state.is_anonymous` accordingly.
    """

get_routes(templates)

Return the routes this provider mounts inside the admin prefix.

Source code in starlette_admin/auth/base.py
def get_routes(self, templates: Jinja2Templates) -> list[Route]:
    """Return the routes this provider mounts inside the admin prefix."""
    return []

starlette_admin.auth.password.AuthProvider

Bases: BaseAuthProvider

Subclass to use the built-in username/password login page.

Implement login, logout, and authenticate only; the framework owns the form, rendering, and redirect.

Source code in starlette_admin/auth/password.py
class AuthProvider(BaseAuthProvider):
    """
    Subclass to use the built-in username/password login page.

    Implement `login`, `logout`, and `authenticate` only;
    the framework owns the form, rendering, and redirect.
    """

    async def login(
        self,
        username: str,
        password: str,
        remember_me: bool,
        request: Request,
    ) -> Response | None:
        """
        Validate credentials and persist auth state (e.g. write to request.session).

        Raise `LoginFailed` for wrong credentials, or `FormValidationError` to mark
        specific form fields invalid.

        Returns:
            A `Response` to redirect to a custom URL, or `None` to fall back to the
            default redirect (`next` query param, or the admin index).

        Examples:
            ```python
            async def login(self, username, password, remember_me, request):
                user = await db.get_user(username)
                if user and verify_password(password, user.hashed_password):
                    request.session["user_id"] = user.id
                    return
                raise LoginFailed("Invalid username or password")
            ```
        """
        raise LoginFailed("Not Implemented")

    async def logout(self, request: Request) -> Response | None:
        """
        Clear auth state (e.g. request.session.clear()).

        Returns:
            A `Response` to redirect to a custom URL, or `None` to fall back to the
            default redirect (the admin index).

        Examples:
            ```python
            async def logout(self, request):
                request.session.clear()
            ```
        """
        raise NotImplementedError()

    async def authenticate(self, request: Request) -> AdminUser | None:
        return None

    async def render_login(
        self, request: Request, templates: Jinja2Templates
    ) -> Response:
        """Render and handle the built-in username/password login page."""
        if getattr(request.state, "admin_user", None) is not None:
            fallback = index_url(request)
            next_url = safe_redirect_url(
                request.query_params.get("next") or fallback,
                request,
                fallback,
            )
            _log.debug(
                "login page: already authenticated, redirecting to %s",
                request.client.host if request.client else "unknown",
            )
            return RedirectResponse(
                next_url,
                status_code=HTTP_303_SEE_OTHER,
            )
        if request.method == "GET":
            _log.debug(
                "login page: GET %s from %s",
                request.url.path,
                request.client.host if request.client else "unknown",
            )
            return templates.TemplateResponse(
                request=request,
                name="login.html",
            )

        form = await request.form()
        username = str(form.get("username") or "")
        remember_me = form.get("remember_me") == "on"
        fallback = index_url(request)
        next_url = safe_redirect_url(
            request.query_params.get("next") or fallback,
            request,
            fallback,
        )
        _log.info(
            "login attempt: username=%r remember_me=%s next=%r from %s",
            username,
            remember_me,
            next_url,
            request.client.host if request.client else "unknown",
        )
        try:
            result = await self.login(
                username,
                str(form.get("password") or ""),
                remember_me,
                request,
            )
            _log.info(
                "login success: username=%r redirect_to=%r from %s",
                username,
                next_url,
                request.client.host if request.client else "unknown",
            )
            await self._emit_after_login(request, await self.authenticate(request))
            return result or RedirectResponse(next_url, status_code=HTTP_303_SEE_OTHER)
        except FormValidationError as errors:
            _log.warning(
                "login form invalid: username=%r fields=%r from %s",
                username,
                list(errors.errors.keys()),
                request.client.host if request.client else "unknown",
            )
            return templates.TemplateResponse(
                request=request,
                name="login.html",
                context={"form_errors": errors},
                status_code=HTTP_422,
            )
        except LoginFailed as error:
            _log.warning(
                "login failed: username=%r reason=%r from %s",
                username,
                error.msg,
                request.client.host if request.client else "unknown",
            )
            return templates.TemplateResponse(
                request=request,
                name="login.html",
                context={"error": error.msg},
                status_code=HTTP_400_BAD_REQUEST,
            )

    async def render_logout(self, request: Request) -> Response:
        """Call `logout()`, then redirect to the admin index."""
        user = getattr(request.state, "user", None)
        _log.info(
            "logout initiated: user=%r from %s",
            user,
            request.client.host if request.client else "unknown",
        )
        result = await self.logout(request)
        _log.info(
            "logout complete: user=%r from %s",
            user,
            request.client.host if request.client else "unknown",
        )
        return result or RedirectResponse(
            index_url(request),
            status_code=HTTP_303_SEE_OTHER,
        )

    def get_routes(self, templates: Jinja2Templates) -> list[Route]:
        return [
            Route(
                self.login_path,
                wrap_endpoint_with_kwargs(self.render_login, templates=templates),
                methods=["GET", "POST"],
                name="login",
            ),
            Route(
                self.logout_path, self.render_logout, methods=["POST"], name="logout"
            ),
        ]

login(username, password, remember_me, request) async

Validate credentials and persist auth state (e.g. write to request.session).

Raise LoginFailed for wrong credentials, or FormValidationError to mark specific form fields invalid.

Returns:

Type Description
Response | None

A Response to redirect to a custom URL, or None to fall back to the

Response | None

default redirect (next query param, or the admin index).

Examples:

async def login(self, username, password, remember_me, request):
    user = await db.get_user(username)
    if user and verify_password(password, user.hashed_password):
        request.session["user_id"] = user.id
        return
    raise LoginFailed("Invalid username or password")
Source code in starlette_admin/auth/password.py
async def login(
    self,
    username: str,
    password: str,
    remember_me: bool,
    request: Request,
) -> Response | None:
    """
    Validate credentials and persist auth state (e.g. write to request.session).

    Raise `LoginFailed` for wrong credentials, or `FormValidationError` to mark
    specific form fields invalid.

    Returns:
        A `Response` to redirect to a custom URL, or `None` to fall back to the
        default redirect (`next` query param, or the admin index).

    Examples:
        ```python
        async def login(self, username, password, remember_me, request):
            user = await db.get_user(username)
            if user and verify_password(password, user.hashed_password):
                request.session["user_id"] = user.id
                return
            raise LoginFailed("Invalid username or password")
        ```
    """
    raise LoginFailed("Not Implemented")

logout(request) async

Clear auth state (e.g. request.session.clear()).

Returns:

Type Description
Response | None

A Response to redirect to a custom URL, or None to fall back to the

Response | None

default redirect (the admin index).

Examples:

async def logout(self, request):
    request.session.clear()
Source code in starlette_admin/auth/password.py
async def logout(self, request: Request) -> Response | None:
    """
    Clear auth state (e.g. request.session.clear()).

    Returns:
        A `Response` to redirect to a custom URL, or `None` to fall back to the
        default redirect (the admin index).

    Examples:
        ```python
        async def logout(self, request):
            request.session.clear()
        ```
    """
    raise NotImplementedError()

render_login(request, templates) async

Render and handle the built-in username/password login page.

Source code in starlette_admin/auth/password.py
async def render_login(
    self, request: Request, templates: Jinja2Templates
) -> Response:
    """Render and handle the built-in username/password login page."""
    if getattr(request.state, "admin_user", None) is not None:
        fallback = index_url(request)
        next_url = safe_redirect_url(
            request.query_params.get("next") or fallback,
            request,
            fallback,
        )
        _log.debug(
            "login page: already authenticated, redirecting to %s",
            request.client.host if request.client else "unknown",
        )
        return RedirectResponse(
            next_url,
            status_code=HTTP_303_SEE_OTHER,
        )
    if request.method == "GET":
        _log.debug(
            "login page: GET %s from %s",
            request.url.path,
            request.client.host if request.client else "unknown",
        )
        return templates.TemplateResponse(
            request=request,
            name="login.html",
        )

    form = await request.form()
    username = str(form.get("username") or "")
    remember_me = form.get("remember_me") == "on"
    fallback = index_url(request)
    next_url = safe_redirect_url(
        request.query_params.get("next") or fallback,
        request,
        fallback,
    )
    _log.info(
        "login attempt: username=%r remember_me=%s next=%r from %s",
        username,
        remember_me,
        next_url,
        request.client.host if request.client else "unknown",
    )
    try:
        result = await self.login(
            username,
            str(form.get("password") or ""),
            remember_me,
            request,
        )
        _log.info(
            "login success: username=%r redirect_to=%r from %s",
            username,
            next_url,
            request.client.host if request.client else "unknown",
        )
        await self._emit_after_login(request, await self.authenticate(request))
        return result or RedirectResponse(next_url, status_code=HTTP_303_SEE_OTHER)
    except FormValidationError as errors:
        _log.warning(
            "login form invalid: username=%r fields=%r from %s",
            username,
            list(errors.errors.keys()),
            request.client.host if request.client else "unknown",
        )
        return templates.TemplateResponse(
            request=request,
            name="login.html",
            context={"form_errors": errors},
            status_code=HTTP_422,
        )
    except LoginFailed as error:
        _log.warning(
            "login failed: username=%r reason=%r from %s",
            username,
            error.msg,
            request.client.host if request.client else "unknown",
        )
        return templates.TemplateResponse(
            request=request,
            name="login.html",
            context={"error": error.msg},
            status_code=HTTP_400_BAD_REQUEST,
        )

render_logout(request) async

Call logout(), then redirect to the admin index.

Source code in starlette_admin/auth/password.py
async def render_logout(self, request: Request) -> Response:
    """Call `logout()`, then redirect to the admin index."""
    user = getattr(request.state, "user", None)
    _log.info(
        "logout initiated: user=%r from %s",
        user,
        request.client.host if request.client else "unknown",
    )
    result = await self.logout(request)
    _log.info(
        "logout complete: user=%r from %s",
        user,
        request.client.host if request.client else "unknown",
    )
    return result or RedirectResponse(
        index_url(request),
        status_code=HTTP_303_SEE_OTHER,
    )

starlette_admin.auth.oauth.OAuthProvider

Bases: BaseAuthProvider

Subclass to use an OAuth2/OIDC flow.

Implement redirect_to_provider, handle_callback, and authenticate. get_routes() returns the login redirect, logout, and callback routes. The callback is automatically added to allow_routes so the middleware never blocks it.

Requires SessionMiddleware: the OAuth client stores the state nonce in the session between the redirect and the callback.

Examples:

from authlib.integrations.starlette_client import OAuth

oauth = OAuth()
oauth.register(
    "provider",
    client_id=CLIENT_ID,
    client_secret=CLIENT_SECRET,
    client_kwargs={"scope": "openid profile email"},
    server_metadata_url=SERVER_METADATA_URL,
)


class OIDCAuthProvider(OAuthProvider):
    async def redirect_to_provider(self, request: Request, callback_url: str) -> Response:
        client = oauth.create_client("provider")
        return await client.authorize_redirect(request, callback_url)

    async def handle_callback(self, request: Request) -> None:
        client = oauth.create_client("provider")
        token = await client.authorize_access_token(request)
        # Persist userinfo in the encrypted session cookie.
        request.session["user"] = dict(token["userinfo"])

    async def authenticate(self, request: Request) -> AdminUser | None:
        user = request.session.get("user")
        if user:
            return AdminUser(
                username=user.get("name") or user.get("email"),
                photo_url=user.get("picture"),
            )
        return None

    async def logout(self, request: Request) -> Response | None:
        request.session.clear()
        # Return a Response to redirect to the provider's end_session_endpoint,
        # or None to fall back to the default admin index redirect.
        return None
Source code in starlette_admin/auth/oauth.py
class OAuthProvider(BaseAuthProvider):
    """
    Subclass to use an OAuth2/OIDC flow.

    Implement `redirect_to_provider`, `handle_callback`, and `authenticate`.
    `get_routes()` returns the login redirect, logout, and callback routes.
    The callback is automatically added to `allow_routes` so the middleware
    never blocks it.

    Requires `SessionMiddleware`: the OAuth client stores the state nonce in the
    session between the redirect and the callback.

    Examples:
        ```python
        from authlib.integrations.starlette_client import OAuth

        oauth = OAuth()
        oauth.register(
            "provider",
            client_id=CLIENT_ID,
            client_secret=CLIENT_SECRET,
            client_kwargs={"scope": "openid profile email"},
            server_metadata_url=SERVER_METADATA_URL,
        )


        class OIDCAuthProvider(OAuthProvider):
            async def redirect_to_provider(self, request: Request, callback_url: str) -> Response:
                client = oauth.create_client("provider")
                return await client.authorize_redirect(request, callback_url)

            async def handle_callback(self, request: Request) -> None:
                client = oauth.create_client("provider")
                token = await client.authorize_access_token(request)
                # Persist userinfo in the encrypted session cookie.
                request.session["user"] = dict(token["userinfo"])

            async def authenticate(self, request: Request) -> AdminUser | None:
                user = request.session.get("user")
                if user:
                    return AdminUser(
                        username=user.get("name") or user.get("email"),
                        photo_url=user.get("picture"),
                    )
                return None

            async def logout(self, request: Request) -> Response | None:
                request.session.clear()
                # Return a Response to redirect to the provider's end_session_endpoint,
                # or None to fall back to the default admin index redirect.
                return None
        ```
    """

    def __init__(
        self,
        login_path: str = "/login",
        logout_path: str = "/logout",
        callback_path: str = "/oauth/callback",
        allow_routes: list[str] | None = None,
    ) -> None:
        super().__init__(login_path, logout_path, allow_routes)
        self.callback_path = callback_path
        # The callback is always public: the OAuth provider redirects here from
        # outside the app, before a session exists.
        self.allow_routes.append("oauth_callback")

    @abstractmethod
    async def redirect_to_provider(
        self, request: Request, callback_url: str
    ) -> Response:
        """Return a redirect response to start the OAuth2 flow."""

    @abstractmethod
    async def handle_callback(self, request: Request) -> None:
        """Exchange the authorization code and write auth state (e.g. to session)."""

    async def logout(self, request: Request) -> Response | None:
        """Clear auth state on logout.

        Returns:
            A `Response` to redirect to a custom URL (e.g. the OIDC
            `end_session_endpoint`), or `None` to use the default index redirect.
        """
        return None

    async def render_login(self, request: Request) -> Response:
        """Redirect to the OAuth provider's authorization URL."""
        route_name = request.app.state.ROUTE_NAME
        callback_url = request.url_for(route_name + ":oauth_callback")
        if next_url := request.query_params.get("next"):
            callback_url = callback_url.include_query_params(next=next_url)
        return await self.redirect_to_provider(request, str(callback_url))

    async def render_logout(self, request: Request) -> Response:
        """Call `logout()`, then redirect to its result or to the admin index."""
        result = await self.logout(request)
        return (
            RedirectResponse(
                index_url(request),
                status_code=HTTP_303_SEE_OTHER,
            )
            if result is None
            else result
        )

    @login_not_required
    async def render_callback(self, request: Request) -> Response:
        """Handle the OAuth callback, then redirect to next or the admin index."""
        await self.handle_callback(request)
        await self._emit_after_login(request, await self.authenticate(request))
        fallback = index_url(request)
        next_url = safe_redirect_url(
            request.query_params.get("next") or fallback,
            request,
            fallback,
        )
        return RedirectResponse(next_url, status_code=HTTP_303_SEE_OTHER)

    def get_routes(self, templates: Jinja2Templates) -> list[Route]:
        return [
            Route(self.login_path, self.render_login, methods=["GET"], name="login"),
            Route(
                self.logout_path, self.render_logout, methods=["POST"], name="logout"
            ),
            Route(
                self.callback_path,
                self.render_callback,
                methods=["GET"],
                name="oauth_callback",
            ),
        ]

handle_callback(request) abstractmethod async

Exchange the authorization code and write auth state (e.g. to session).

Source code in starlette_admin/auth/oauth.py
@abstractmethod
async def handle_callback(self, request: Request) -> None:
    """Exchange the authorization code and write auth state (e.g. to session)."""

logout(request) async

Clear auth state on logout.

Returns:

Type Description
Response | None

A Response to redirect to a custom URL (e.g. the OIDC

Response | None

end_session_endpoint), or None to use the default index redirect.

Source code in starlette_admin/auth/oauth.py
async def logout(self, request: Request) -> Response | None:
    """Clear auth state on logout.

    Returns:
        A `Response` to redirect to a custom URL (e.g. the OIDC
        `end_session_endpoint`), or `None` to use the default index redirect.
    """
    return None

redirect_to_provider(request, callback_url) abstractmethod async

Return a redirect response to start the OAuth2 flow.

Source code in starlette_admin/auth/oauth.py
@abstractmethod
async def redirect_to_provider(
    self, request: Request, callback_url: str
) -> Response:
    """Return a redirect response to start the OAuth2 flow."""

render_callback(request) async

Handle the OAuth callback, then redirect to next or the admin index.

Source code in starlette_admin/auth/oauth.py
@login_not_required
async def render_callback(self, request: Request) -> Response:
    """Handle the OAuth callback, then redirect to next or the admin index."""
    await self.handle_callback(request)
    await self._emit_after_login(request, await self.authenticate(request))
    fallback = index_url(request)
    next_url = safe_redirect_url(
        request.query_params.get("next") or fallback,
        request,
        fallback,
    )
    return RedirectResponse(next_url, status_code=HTTP_303_SEE_OTHER)

render_login(request) async

Redirect to the OAuth provider's authorization URL.

Source code in starlette_admin/auth/oauth.py
async def render_login(self, request: Request) -> Response:
    """Redirect to the OAuth provider's authorization URL."""
    route_name = request.app.state.ROUTE_NAME
    callback_url = request.url_for(route_name + ":oauth_callback")
    if next_url := request.query_params.get("next"):
        callback_url = callback_url.include_query_params(next=next_url)
    return await self.redirect_to_provider(request, str(callback_url))

render_logout(request) async

Call logout(), then redirect to its result or to the admin index.

Source code in starlette_admin/auth/oauth.py
async def render_logout(self, request: Request) -> Response:
    """Call `logout()`, then redirect to its result or to the admin index."""
    result = await self.logout(request)
    return (
        RedirectResponse(
            index_url(request),
            status_code=HTTP_303_SEE_OTHER,
        )
        if result is None
        else result
    )

starlette_admin.auth.base.AdminUser dataclass

Source code in starlette_admin/auth/base.py
@dataclass
class AdminUser:
    username: str = field(default_factory=lambda: _("Administrator"))
    photo_url: str | None = None

starlette_admin.auth.base.AuthMiddleware

Bases: BaseHTTPMiddleware

Source code in starlette_admin/auth/base.py
class AuthMiddleware(BaseHTTPMiddleware):
    def __init__(self, app: ASGIApp, provider: "BaseAuthProvider") -> None:
        super().__init__(app)
        self.provider = provider
        self.allow_routes: list[str] = ["login", "static", *provider.allow_routes]
        _log.debug(
            "AuthMiddleware init: provider=%s allow_routes=%r",
            type(provider).__name__,
            self.allow_routes,
        )

    async def dispatch(
        self, request: Request, call_next: RequestResponseEndpoint
    ) -> Response:
        """Enforce authentication for every request that passes through this middleware.

        Allows the request through when one of the following holds: the provider
        authenticates the user (`authenticate()` returns an `AdminUser`), the matched
        route name is in `allow_routes`, or the endpoint is decorated with
        `@login_not_required`. Otherwise, redirects to the login page.
        """
        _admin_app: Starlette = request.scope["app"]
        current_route: Route | Mount | WebSocketRoute | None = None
        for route in _admin_app.routes:
            match, _ = route.matches(request.scope)
            if match == Match.FULL:
                if not isinstance(
                    route, (Route, Mount, WebSocketRoute)
                ):  # pragma: no cover
                    raise TypeError(f"Unexpected route type {type(route).__name__}")
                current_route = route
                break

        route_name = getattr(current_route, "name", None)
        route_path = getattr(current_route, "path", None)
        _log.debug(
            "auth check: %s %s  route=%r path=%r from %s",
            request.method,
            request.url.path,
            route_name,
            route_path,
            request.client.host if request.client else "unknown",
        )

        user = await self.provider.authenticate(request)

        if user is not None:
            if not isinstance(user, AdminUser):
                raise TypeError(
                    f"authenticate() must return an AdminUser instance, "
                    f"got {type(user).__name__}"
                )
            request.state.admin_user = user
            request.state.is_anonymous = False
            _log.debug(
                "auth: authenticated user=%r route=%r %s %s",
                user,
                route_name,
                request.method,
                request.url.path,
            )
            return await call_next(request)

        request.state.is_anonymous = True

        if current_route is not None and current_route.name in self.allow_routes:
            _log.debug(
                "auth: allowed (route in allow_routes) route=%r",
                route_name,
            )
            return await call_next(request)

        if (
            current_route is not None
            and hasattr(current_route, "endpoint")
            and getattr(current_route.endpoint, "_login_not_required", False)
        ):
            _log.debug(
                "auth: allowed (@login_not_required) route=%r",
                route_name,
            )
            return await call_next(request)

        login_url = "{url}?{query_params}".format(
            url=request.url_for(request.app.state.ROUTE_NAME + ":login"),
            query_params=urlencode({"next": str(request.url)}),
        )
        _log.warning(
            "auth: unauthenticated, redirecting to login: %s %s -> %s from %s",
            request.method,
            request.url.path,
            login_url,
            request.client.host if request.client else "unknown",
        )
        return RedirectResponse(login_url, status_code=HTTP_303_SEE_OTHER)

dispatch(request, call_next) async

Enforce authentication for every request that passes through this middleware.

Allows the request through when one of the following holds: the provider authenticates the user (authenticate() returns an AdminUser), the matched route name is in allow_routes, or the endpoint is decorated with @login_not_required. Otherwise, redirects to the login page.

Source code in starlette_admin/auth/base.py
async def dispatch(
    self, request: Request, call_next: RequestResponseEndpoint
) -> Response:
    """Enforce authentication for every request that passes through this middleware.

    Allows the request through when one of the following holds: the provider
    authenticates the user (`authenticate()` returns an `AdminUser`), the matched
    route name is in `allow_routes`, or the endpoint is decorated with
    `@login_not_required`. Otherwise, redirects to the login page.
    """
    _admin_app: Starlette = request.scope["app"]
    current_route: Route | Mount | WebSocketRoute | None = None
    for route in _admin_app.routes:
        match, _ = route.matches(request.scope)
        if match == Match.FULL:
            if not isinstance(
                route, (Route, Mount, WebSocketRoute)
            ):  # pragma: no cover
                raise TypeError(f"Unexpected route type {type(route).__name__}")
            current_route = route
            break

    route_name = getattr(current_route, "name", None)
    route_path = getattr(current_route, "path", None)
    _log.debug(
        "auth check: %s %s  route=%r path=%r from %s",
        request.method,
        request.url.path,
        route_name,
        route_path,
        request.client.host if request.client else "unknown",
    )

    user = await self.provider.authenticate(request)

    if user is not None:
        if not isinstance(user, AdminUser):
            raise TypeError(
                f"authenticate() must return an AdminUser instance, "
                f"got {type(user).__name__}"
            )
        request.state.admin_user = user
        request.state.is_anonymous = False
        _log.debug(
            "auth: authenticated user=%r route=%r %s %s",
            user,
            route_name,
            request.method,
            request.url.path,
        )
        return await call_next(request)

    request.state.is_anonymous = True

    if current_route is not None and current_route.name in self.allow_routes:
        _log.debug(
            "auth: allowed (route in allow_routes) route=%r",
            route_name,
        )
        return await call_next(request)

    if (
        current_route is not None
        and hasattr(current_route, "endpoint")
        and getattr(current_route.endpoint, "_login_not_required", False)
    ):
        _log.debug(
            "auth: allowed (@login_not_required) route=%r",
            route_name,
        )
        return await call_next(request)

    login_url = "{url}?{query_params}".format(
        url=request.url_for(request.app.state.ROUTE_NAME + ":login"),
        query_params=urlencode({"next": str(request.url)}),
    )
    _log.warning(
        "auth: unauthenticated, redirecting to login: %s %s -> %s from %s",
        request.method,
        request.url.path,
        login_url,
        request.client.host if request.client else "unknown",
    )
    return RedirectResponse(login_url, status_code=HTTP_303_SEE_OTHER)

starlette_admin.auth.base.login_not_required(endpoint)

Mark an endpoint as exempt from the authentication check.

Apply this decorator to a route handler to let unauthenticated requests reach it. AuthMiddleware checks for the _login_not_required attribute set here before redirecting to the login page.

Source code in starlette_admin/auth/base.py
def login_not_required(endpoint: Callable[..., Any]) -> Callable[..., Any]:
    """Mark an endpoint as exempt from the authentication check.

    Apply this decorator to a route handler to let unauthenticated requests reach it.
    `AuthMiddleware` checks for the `_login_not_required` attribute set here before
    redirecting to the login page.
    """
    endpoint._login_not_required = True  # ty: ignore[unresolved-attribute]
    return endpoint