Description
Context
Litestar provides a way to declare global security components, for example by the using of a global security configuration like JWTAuth or by building your own (Example implementation in AbstractSecurityConfig.on_app_init).
This creates both a global security component and requirement in your openapi:

According to the openapi v3 security documentation you can override at the security keyword at the operation level:
When used on the root level, security applies the specified security schemes globally to all API operations, unless overridden on the operation level.
The problem
The problem is that you cannot override the security requirement neither at the route or router level with an empty list.
@get(
"/login",
# security=[], # This should override the global security, but is being ignored
)
async def login() -> str:
return "Sample"
Although Litestar provides an API to do so, it does not respect the provided in the security because of how it internally handles empty security requirements.
Note that passing a non empty list like security=[{"SAMPLE": []}] would actually made it to the openapi security requirements of the route.
Technical details of the bug and proposed fix
The bug is effectively here:
|
security=route_handler.resolve_security() or None, |
The or None replaces any empty resolved security requirement with None, removing it.
The problem is that simply removing or None would make all routes have an overridden security keyword, which is clearly not the desirable effect.
My proposal is to change the default from something which represents as it being not set and handle it properly in the resolve_security method.
Should I attempt a fix?
MCVE
`app.py`
from typing import Any
from litestar.security.jwt.auth import JWTAuth
from litestar import Litestar, get
from litestar.connection import ASGIConnection
@get(
"/login",
security=[], # This should override the global security, but is being ignored
)
async def login() -> str:
return "Sample"
@get("/hello-world")
async def hello() -> str:
return "Hello world"
async def retrieve_user_handler(
session: dict[str, Any],
connection: ASGIConnection[Any, Any, Any, Any],
) -> str:
return "ignored"
jwt_auth = JWTAuth(
token_secret="secret",
retrieve_user_handler=retrieve_user_handler,
exclude=[
"/login",
"/schema",
],
)
app = Litestar(
route_handlers=[login, hello],
on_app_init=[jwt_auth.on_app_init],
)
# How to run this:
# Instal dependencies with pip install litestar[full] uvicorn
# Start the server with `python -m uvicorn app:app --port 8000 --reload``
Steps to reproduce
1. Run the above code.
2. See that the route `/login` has the security in the swagger UI `http://localhost:8000/schema/swagger/` instead of having been overridden by the `security` route argument at line `11`.
Screenshots
Expected result:

Actual:

Litestar Version
2.14.0
Platform
Description
Context
Litestar provides a way to declare global security components, for example by the using of a global security configuration like
JWTAuthor by building your own (Example implementation inAbstractSecurityConfig.on_app_init).This creates both a global security component and requirement in your openapi:
According to the openapi v3 security documentation you can override at the security keyword at the operation level:
The problem
The problem is that you cannot override the security requirement neither at the route or router level with an empty list.
Although Litestar provides an API to do so, it does not respect the provided in the
securitybecause of how it internally handles empty security requirements.Note that passing a non empty list like
security=[{"SAMPLE": []}]would actually made it to the openapi security requirements of the route.Technical details of the bug and proposed fix
The bug is effectively here:
litestar/litestar/_openapi/path_item.py
Line 86 in 6768845
The
or Nonereplaces any empty resolved security requirement with None, removing it.The problem is that simply removing
or Nonewould make all routes have an overridden security keyword, which is clearly not the desirable effect.My proposal is to change the default from something which represents as it being not set and handle it properly in the resolve_security method.
Should I attempt a fix?
MCVE
`app.py`
Steps to reproduce
Screenshots
Expected result:
Actual:
Litestar Version
2.14.0
Platform