diff options
| author | Tom Flux <tom@tomflux.xyz> | 2026-06-23 20:37:48 +0100 |
|---|---|---|
| committer | Tom Flux <tom@tomflux.xyz> | 2026-06-23 20:37:48 +0100 |
| commit | d92deeab514a52e7c3416baef78f8969ade951cc (patch) | |
| tree | f5c071147ea6f62501dee904a1f381805a1e5941 /kitchen/templates | |
| parent | 682c0f76b85950ad0889e11750dc84e0f00d2807 (diff) | |
Phase 2: session auth for the web UI
Require login for /app/, with a long sliding session so you log in
once per device and effectively stay in.
- AppLoginRequiredMiddleware gates only /app/; /api/ keeps DRF token
auth and /admin/ keeps its own login (a blanket LoginRequired would
break token requests, whose user isn't resolved until the view runs).
- Login page (styled to the dark palette) via django.contrib.auth.urls;
logout control in the nav.
- Session: ~1 year cookie, sliding (saved every request), survives
browser close.
- Dropped every @csrf_exempt now that a real session + CSRF token are
in place (HTMX already sends X-CSRFToken).
- SECRET_KEY and DEBUG now read from the environment (prod-safe
defaults); systemd loads an optional /var/lib/food/.env.
- Tests authenticate, plus new coverage: /app/ redirects when logged
out, login grants access, /api/ is not caught by the app gate.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diffstat (limited to 'kitchen/templates')
| -rw-r--r-- | kitchen/templates/kitchen/base.html | 4 | ||||
| -rw-r--r-- | kitchen/templates/registration/login.html | 116 |
2 files changed, 120 insertions, 0 deletions
diff --git a/kitchen/templates/kitchen/base.html b/kitchen/templates/kitchen/base.html index 323e985..92faedc 100644 --- a/kitchen/templates/kitchen/base.html +++ b/kitchen/templates/kitchen/base.html @@ -409,6 +409,10 @@ <a href="{% url 'app-shopping' %}" {% if active_tab == 'shopping' %}class="active"{% endif %}>Shopping</a> <a href="{% url 'app-log' %}" {% if active_tab == 'log' %}class="active"{% endif %}>Cook Log</a> </div> + <form method="post" action="{% url 'logout' %}" style="margin-left: auto;"> + {% csrf_token %} + <button type="submit" style="background: none; border: 1px solid var(--teal-dark); color: var(--grey-light); padding: 0.4rem 0.75rem; border-radius: 4px; font-size: 0.85rem; cursor: pointer;">Log out</button> + </form> </nav> <div class="container"> diff --git a/kitchen/templates/registration/login.html b/kitchen/templates/registration/login.html new file mode 100644 index 0000000..4d0e1aa --- /dev/null +++ b/kitchen/templates/registration/login.html @@ -0,0 +1,116 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Sign in — Kitchen</title> + <style> + :root { + --bg-dark: #15131c; + --navy: #0e0e5b; + --teal-dark: #325664; + --sage: #658d89; + --red-bright: #f01111; + --yellow: #f9df11; + --teal-light: #87d1d1; + --grey-light: #babcc4; + --cream: #f7fdc7; + } + * { margin: 0; padding: 0; box-sizing: border-box; } + body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; + background: var(--bg-dark); + color: var(--grey-light); + min-height: 100vh; + display: flex; + align-items: center; + justify-content: center; + padding: 1.5rem; + } + .login-card { + width: 100%; + max-width: 360px; + background: rgba(50, 86, 100, 0.15); + border: 1px solid var(--teal-dark); + border-radius: 10px; + padding: 1.75rem 1.5rem; + } + .logo { + font-size: 1.4rem; + font-weight: 700; + color: var(--yellow); + margin-bottom: 1.25rem; + } + label { + display: block; + color: var(--teal-light); + font-size: 0.78rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; + margin: 0 0 0.3rem; + } + input { + width: 100%; + background: rgba(14, 14, 91, 0.3); + border: 1px solid var(--teal-dark); + color: var(--cream); + padding: 0.7rem 0.8rem; + border-radius: 8px; + font-size: 1rem; + min-height: 48px; + } + input:focus { + outline: none; + border-color: var(--yellow); + box-shadow: 0 0 0 2px rgba(249, 223, 17, 0.15); + } + .field { margin-bottom: 1rem; } + .btn { + width: 100%; + background: var(--yellow); + color: var(--bg-dark); + border: none; + font-size: 1rem; + font-weight: 700; + padding: 0.8rem; + border-radius: 8px; + cursor: pointer; + min-height: 48px; + } + .btn:active { transform: translateY(1px); } + .errors { + background: rgba(240, 17, 17, 0.14); + color: var(--red-bright); + border-radius: 8px; + padding: 0.6rem 0.8rem; + font-size: 0.85rem; + margin-bottom: 1rem; + } + </style> +</head> +<body> + <form class="login-card" method="post" action="{% url 'login' %}"> + {% csrf_token %} + <div class="logo">🍳 Kitchen</div> + + {% if form.errors %} + <div class="errors">That username and password didn't match. Try again.</div> + {% endif %} + + <div class="field"> + <label for="id_username">Username</label> + <input type="text" name="username" id="id_username" autocapitalize="none" + autocomplete="username" autofocus required> + </div> + <div class="field"> + <label for="id_password">Password</label> + <input type="password" name="password" id="id_password" + autocomplete="current-password" required> + </div> + + <input type="hidden" name="next" value="{{ next }}"> + <button type="submit" class="btn">Sign in</button> + </form> +</body> +</html> |
