diff --git a/muxplex/main.py b/muxplex/main.py
index fb2a0da..12070d2 100644
--- a/muxplex/main.py
+++ b/muxplex/main.py
@@ -9,6 +9,7 @@ Background poll loop reconciles tmux session state every POLL_INTERVAL seconds.
import asyncio
import contextlib
+import json
import logging
import os
import pathlib
@@ -266,6 +267,13 @@ class HeartbeatPayload(BaseModel):
last_interaction_at: float
+# ---------------------------------------------------------------------------
+# Frontend directory
+# ---------------------------------------------------------------------------
+
+_FRONTEND_DIR = pathlib.Path(__file__).parent / "frontend"
+
+
# ---------------------------------------------------------------------------
# Routes
# ---------------------------------------------------------------------------
@@ -476,16 +484,14 @@ async def terminal_ws_proxy(websocket: WebSocket) -> None:
@app.get("/login", response_class=HTMLResponse)
async def login_page():
- """Stub login page — replaced in Phase 2 with branded login.html."""
- return HTMLResponse(
- "
"
- "muxplex login
"
- ""
- ""
+ """Serve branded login.html with injected window.MUXPLEX_AUTH containing auth mode and username."""
+ html = (_FRONTEND_DIR / "login.html").read_text()
+ username = pwd.getpwuid(os.getuid()).pw_name if _auth_mode == "pam" else ""
+ mode_data = json.dumps({"mode": _auth_mode, "user": username})
+ html = html.replace(
+ "", f""
)
+ return HTMLResponse(html)
@app.post("/login")
@@ -545,5 +551,4 @@ async def auth_mode_endpoint():
# Static file serving — MUST come after all API routes (first-match-wins)
# ---------------------------------------------------------------------------
-_FRONTEND_DIR = pathlib.Path(__file__).parent / "frontend"
app.mount("/", StaticFiles(directory=str(_FRONTEND_DIR), html=True), name="frontend")
diff --git a/muxplex/tests/test_api.py b/muxplex/tests/test_api.py
index 189a6df..0a543ea 100644
--- a/muxplex/tests/test_api.py
+++ b/muxplex/tests/test_api.py
@@ -707,6 +707,14 @@ def test_get_auth_mode_returns_json(client):
assert data["mode"] in ("pam", "password")
+def test_get_login_injects_muxplex_auth(client):
+ """GET /login returns 200 with MUXPLEX_AUTH injected into HTML."""
+ response = client.get("/login")
+ assert response.status_code == 200
+ assert "MUXPLEX_AUTH" in response.text
+ assert '"mode"' in response.text
+
+
# ---------------------------------------------------------------------------
# POST /login
# ---------------------------------------------------------------------------