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 |
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 dataPOST /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"withhx-target="#profile-status"andhx-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 withhttp://orhttps://)- String length limits enforced server-side
- HTML5 input attributes for client-side hints (
type="email",type="url",maxlength)
Data Flow
- GET:
get_session_user()→user_repo.get_by_id(userid)→ render template with user fields - 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)