Properties
category: spec
tags: [ux, auth, login, plan]
last_updated: 2026-03-17
confidence: high

Login Page UX Improvement Plan

Two independent changes: (1) route-level logic to detect and act on an existing valid JWT cookie, and (2) visual redesign of the auth templates to match the landing page. No ATProto OAuth flow changes required.

1. JWT Detection and Auto-Redirect

Where: oauth_login() GET branch in auth_server.py.

Logic:

  1. Read request.cookies.get(COOKIE_NAME)
  2. If present, call platform_jwt.validate_token(cookie_token)
  3. On success: redirect to return_to (if set and safe) or /app/
  4. On ExpiredSignatureError: decode without verification to extract handle claim, pass as prefill_handle to template
  5. On any other error: fall through to render login form normally

The login template pre-populates <input name="username"> with prefill_handle if provided.

2. Visual Redesign — Match Landing Page

Current state: Auth pages use Pico CSS (purple, dark theme). Landing page uses custom style.css (light, minimal, system fonts). Management panel uses Halfmoon/Bootstrap.

Approach: Replace Pico CSS in app/auth/templates/base.html with the landing page's style.css. Use same <div class="page"> wrapper and <header>/<footer> structure as index.html. Add minimal form/button styles (inline or in style.css). Consent page also extends base.html — benefits automatically.

Files to Modify

File Change
app/auth_server.py JWT cookie check + pre-fill logic in GET branch of oauth_login()
app/auth/templates/base.html Replace Pico CSS with style.css, match landing page HTML structure
app/auth/templates/login.html Add value="{{ prefill_handle or '' }}" to username input
static/style.css Add minimal form/button styles (or use inline block in base.html)

Test Plan

  1. test_login_get_with_valid_jwt_redirects_to_dashboard — valid cookie → 302 to /app/
  2. test_login_get_with_valid_jwt_and_return_to_redirects — valid cookie + return_to → redirect
  3. test_login_get_with_expired_jwt_prefills_handle — expired cookie → 200 with handle pre-filled
  4. test_login_get_with_invalid_jwt_shows_form_blank — garbage cookie → 200, no pre-fill
  5. Visual regression: manual review against landing page

Sequencing

  1. Route logic (auth_server.py) — self-contained, testable immediately
  2. Template changes (base.html, login.html) — same or separate commit
  3. Optional: form styles in style.css (shared file, deserves own review)
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9