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:
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user