# Auth Phase 2: Login UI + CLI — Implementation Plan > **Execution:** Use the subagent-driven-development workflow to implement this plan. **Goal:** Build the branded login page, complete login/logout routes, and add all CLI auth commands (`--auth`, `--session-ttl`, `show-password`, `reset-secret`, startup logging). **Architecture:** Phase 1 established `auth.py` (middleware, password/secret/cookie/PAM functions) and a stub `/login` route. Phase 2 replaces the stub with a fully branded `login.html` that auto-detects PAM vs password mode, adds the POST `/login` and GET `/auth/logout` handlers, and wires the CLI flags and subcommands that control auth behavior at startup. **Tech Stack:** Python 3.11+, FastAPI, HTML/CSS/JS (no framework), argparse, pytest **Phase:** 2 of 2 — complete Phase 1 (`2026-03-28-auth-phase1-infrastructure.md`) before starting this phase. **Design doc:** `docs/plans/2026-03-28-auth-design.md` **Prerequisite:** Phase 1 must be complete. Verify: `python -m pytest muxplex/tests/ -v` — all tests pass, `muxplex/auth.py` exists with `AuthMiddleware`, `/login` stub and `/auth/mode` endpoint exist in `main.py`. --- ### Task 1: Create branded login.html **Files:** - Create: `muxplex/frontend/login.html` - Modify: `muxplex/tests/test_frontend_html.py` (add login.html tests) **Step 1: Write the failing tests** Append to `muxplex/tests/test_frontend_html.py`: ```python # --------------------------------------------------------------------------- # login.html tests # --------------------------------------------------------------------------- _LOGIN_HTML_PATH = pathlib.Path(__file__).parent.parent / "frontend" / "login.html" def _login_soup() -> BeautifulSoup: """Parse login.html — separate from index.html soup.""" return BeautifulSoup(_LOGIN_HTML_PATH.read_text(), "html.parser") def test_login_html_exists() -> None: """login.html must exist in the frontend directory.""" assert _LOGIN_HTML_PATH.exists(), f"Missing {_LOGIN_HTML_PATH}" def test_login_html_has_form() -> None: """login.html must contain a POST form targeting /login.""" soup = _login_soup() form = soup.find("form") assert form is not None, "Missing