Merge pull request #87 from christianlouis/copilot/exclude-static-assets-login

Exclude static asset file extensions from auth redirect middleware
This commit is contained in:
Christian Krakau-Louis
2026-03-30 18:43:36 +02:00
committed by GitHub
2 changed files with 56 additions and 0 deletions
+20
View File
@@ -37,6 +37,24 @@ _PUBLIC_PREFIXES: tuple[str, ...] = (
"/openapi",
)
# File extensions for static assets that are always publicly accessible
_STATIC_EXTENSIONS: tuple[str, ...] = (
".ico",
".png",
".jpg",
".jpeg",
".gif",
".svg",
".webp",
".css",
".js",
".woff",
".woff2",
".ttf",
".eot",
".map",
)
class AuthRedirectMiddleware(BaseHTTPMiddleware):
"""
@@ -68,6 +86,8 @@ class AuthRedirectMiddleware(BaseHTTPMiddleware):
return await call_next(request)
if any(path.startswith(p) for p in _PUBLIC_PREFIXES):
return await call_next(request)
if any(path.endswith(ext) for ext in _STATIC_EXTENSIONS):
return await call_next(request)
# ── 2. Valid session cookie ───────────────────────────────────────────
token = request.cookies.get(SESSION_COOKIE)
+36
View File
@@ -293,3 +293,39 @@ class TestAuthDisabled:
# never be a 302 redirect from the middleware.
res = client.get("/settings", follow_redirects=False)
assert res.status_code != 302
# ── Static asset bypass ───────────────────────────────────────────────────────
class TestStaticAssetBypass:
"""Static assets must never be redirected to the login page."""
@staticmethod
def _logto_configured_mock():
mock_cfg = MagicMock()
mock_cfg.AUTH_DISABLED = False
mock_cfg.logto_configured = True
return mock_cfg
def test_favicon_not_redirected_to_login(self, client: TestClient):
"""GET /favicon.ico without a session must pass through (not redirect to /login)."""
with patch("app.core.config.get_settings") as mock_get_settings:
mock_get_settings.return_value = self._logto_configured_mock()
res = client.get("/favicon.ico", follow_redirects=False)
assert res.status_code != 302
def test_png_asset_not_redirected_to_login(self, client: TestClient):
"""GET /logo.png without a session must pass through."""
with patch("app.core.config.get_settings") as mock_get_settings:
mock_get_settings.return_value = self._logto_configured_mock()
res = client.get("/logo.png", follow_redirects=False)
assert res.status_code != 302
def test_protected_page_still_redirected(self, client: TestClient):
"""GET /dashboard without a session must still redirect to /login."""
with patch("app.core.config.get_settings") as mock_get_settings:
mock_get_settings.return_value = self._logto_configured_mock()
res = client.get("/dashboard", follow_redirects=False)
assert res.status_code == 302
assert res.headers["location"].startswith("/login")