porchlight/docs/plans/2026-02-18-profile-page-design.md
2026-04-10 11:28:51 +02:00

2.9 KiB

Self-Service Profile Page Design

Overview

Add a /manage/profile page where authenticated users can view and edit their OIDC profile fields. This completes the self-service user management story alongside the existing /manage/credentials page.

Editable Fields

Field Input type Validation
given_name text max 255 chars
family_name text max 255 chars
preferred_username text max 255 chars
email email HTML5 + server-side format check
phone_number tel optional, no strict format
picture url optional, valid URL check
locale text optional, max 20 chars

Read-only: username (login identity, displayed but not editable).

Architecture

Routes

Add to manage/routes.py:

  • GET /manage/profile — render profile form pre-filled with current user data
  • POST /manage/profile — validate and update user, return HTMX partial response

Auth guard follows existing pattern: get_session_user(request) with redirect to /login.

Templates

Create manage/base.html extending base.html with a nav bar containing links to Profile and Credentials. Both manage pages extend this instead of base.html directly.

templates/
  base.html                    (unchanged)
  manage/
    base.html                  (NEW - adds nav)
    credentials.html           (CHANGED - extends manage/base.html)
    profile.html               (NEW - profile form)

Navigation

The manage/base.html template adds a <nav> element with links:

  • Profile (/manage/profile)
  • Credentials (/manage/credentials)

This keeps the nav scoped to authenticated manage pages without modifying the public base.html.

Form Behavior

  • HTMX: hx-post="/manage/profile" with hx-target="#profile-status" and hx-swap="innerHTML"
  • Success: <div role="status">Profile updated</div>
  • Validation error: <div role="alert">specific error message</div>
  • Pre-filled with current values on GET
  • Empty optional fields show empty inputs (not "None")

Validation

Server-side validation in the POST handler:

  • email: if provided, basic format check (contains @)
  • picture: if provided, basic URL format check (starts with http:// or https://)
  • String length limits enforced server-side
  • HTML5 input attributes for client-side hints (type="email", type="url", maxlength)

Data Flow

  1. GET: get_session_user()user_repo.get_by_id(userid) → render template with user fields
  2. POST: get_session_user()user_repo.get_by_id(userid) → validate form data → user.model_copy(update={...})user_repo.update(user) → return status HTML

E2E Tests

tests/e2e/profile.spec.js:

  • Page renders with correct form fields
  • Auth guard redirects unauthenticated users
  • Successful profile update
  • Validation errors (invalid email, etc.)
  • Navigation between profile and credentials
  • Fields pre-filled after update (persistence)