From 2ca44205362a1e5d0ddd8cb8173295e32d15fd74 Mon Sep 17 00:00:00 2001 From: Brian Krabach Date: Sat, 28 Mar 2026 22:30:39 -0700 Subject: [PATCH] feat: add GET /auth/logout route that clears session cookie and redirects to /login - Add @app.get('/auth/logout') handler in muxplex/main.py after POST /login - Creates RedirectResponse('/login', status_code=303) - Calls response.delete_cookie('muxplex_session') to clear the auth cookie - Add two tests in test_api.py: - test_logout_redirects_to_login: verifies 303 status and /login in location header - test_logout_clears_session_cookie: verifies Set-Cookie has muxplex_session with max-age=0 - Route is already exempt from AuthMiddleware via _AUTH_EXEMPT_PATHS in auth.py Co-authored-by: Amplifier --- muxplex/main.py | 8 ++++++++ muxplex/tests/test_api.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/muxplex/main.py b/muxplex/main.py index 953cfc6..fb2a0da 100644 --- a/muxplex/main.py +++ b/muxplex/main.py @@ -524,6 +524,14 @@ async def post_login( return response +@app.get("/auth/logout") +async def logout() -> RedirectResponse: + """Clear the muxplex_session cookie and redirect to /login.""" + response = RedirectResponse("/login", status_code=303) + response.delete_cookie("muxplex_session") + return response + + @app.get("/auth/mode") async def auth_mode_endpoint(): """Return the current auth mode and running username.""" diff --git a/muxplex/tests/test_api.py b/muxplex/tests/test_api.py index 4bad798..03dae28 100644 --- a/muxplex/tests/test_api.py +++ b/muxplex/tests/test_api.py @@ -774,3 +774,32 @@ def test_post_login_pam_mode_wrong_creds(monkeypatch): assert response.status_code == 303 assert "error=1" in response.headers["location"] + + +# --------------------------------------------------------------------------- +# GET /auth/logout +# --------------------------------------------------------------------------- + + +def test_logout_redirects_to_login(monkeypatch): + """GET /auth/logout returns 303 redirect to /login.""" + monkeypatch.setenv("MUXPLEX_PASSWORD", "test-password") + + with TestClient(app, follow_redirects=False) as c: + response = c.get("/auth/logout") + + assert response.status_code == 303 + assert "/login" in response.headers["location"] + + +def test_logout_clears_session_cookie(monkeypatch): + """GET /auth/logout clears muxplex_session cookie (Set-Cookie with max-age=0).""" + monkeypatch.setenv("MUXPLEX_PASSWORD", "test-password") + + with TestClient(app, follow_redirects=False) as c: + response = c.get("/auth/logout") + + assert response.status_code == 303 + set_cookie = response.headers.get("set-cookie", "") + assert "muxplex_session" in set_cookie + assert "max-age=0" in set_cookie.lower()