GET /register/{token} consumed the magic-link token and created a session, so
a side-effecting state change happened on a safe method — link prefetchers,
email scanners, or a cross-site GET could trigger account setup/login.
Split the flow: GET validates the token (without consuming) and renders a
confirmation form; POST /register/{token} consumes the token, runs the
existing checks, and establishes the session. The POST carries a CSRF token
and the session is reset on login as for other auth paths.
Refs: porchlight-9k0
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The full Playwright suite authenticates ~100 times in a few minutes, far
over the login endpoint's 5/minute limit, so most specs failed at the
beforeEach login with 429s.
Add an OIDC_OP_RATE_LIMIT_ENABLED setting (default True) wired to the
slowapi limiter's enabled flag, and set it to false in tests/e2e/run.sh.
Production behavior is unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The password setup/change form used hx-target="#password-section" with
hx-swap="innerHTML", but that div wraps the form itself. On a validation
error the route returns only an alert div, so the swap replaced the entire
form — the password inputs disappeared. Most visible during registration's
"set password" step.
Retarget the form to a dedicated #password-error div outside the form
(mirrors the working login form's #login-error pattern), so the form and
its inputs survive errors while messages still render inside #password-section.
Also fix pre-existing broken e2e tests: they omitted the required
current_password fill and used passwords below the zxcvbn strength
threshold (score 1 < MIN_PASSWORD_STRENGTH=2).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Use Annotated[str, Form()] for FastAPI dependencies (FAST002)
- Add missing type annotations across src/ and tests/ (ANN001/003/201/202)
- Reduce function arguments via request.form() reads (PLR0913)
- Combine return paths to reduce return statements (PLR0911)
- Use anyio.Path for async-safe filesystem operations (ASYNC240)
- Extract constants, helpers, and dict comprehensions for clarity
- Move inline imports to top-level (PLC0415)
- Use raw strings for regex match patterns (RUF043)
- Fix redundant get_session_user call in delete_user (not-iterable)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add 7 new e2e tests verifying profile form validation in both manage
and admin UIs: invalid phone number, phone normalization, E.164 hint
attributes, and admin-side email/phone/picture URL validation errors.
Fix 3 pre-existing test failures:
- Replace invalid seeded phone number (+1234567890) with valid E.164
(+12025551234) that was causing profile update tests to fail
- Update email validation error assertion to match actual pydantic
message (value_error type uses raw message, not label-prefixed)
Add /manage/profile page where authenticated users can view and edit
their OIDC profile fields (given_name, family_name, preferred_username,
email, phone_number, picture, locale).
- Create manage/base.html with tab-style nav for Profile/Credentials
- Update credentials.html to extend manage/base.html
- Add GET/POST routes with server-side validation
- Add input styling for tel and url input types
- Add profile test user with pre-filled data in setup_db.py
- Add 19 E2E tests covering structure, navigation, updates, validation
- All 76 E2E tests and 172 Python tests pass
- Use localhost instead of 127.0.0.1 as TARGET_URL so the WebAuthn RP ID
is a valid domain (the spec forbids IP addresses)
- Replace request.post('/logout') with page.context().clearCookies() since
Playwright's request fixture has a separate cookie jar from the page
- Add registerPasskey() helper that waits for 'load' event to reliably
detect the page reload after successful registration
- Track credential count with getCredentialCount() since credentials
accumulate across serial tests sharing the same database
- Fix login.spec.js selector from #webauthn-login-form to #webauthn-login-btn
to match the actual template
All 57 E2E tests now pass (50 migrated + 7 WebAuthn).
Replace direct playwright dependency with @playwright/test and add
playwright.config.js for centralized test configuration. Update run.sh
to invoke 'npx playwright test' instead of running test files with node.
Extract shared test runner (helpers.js), add file-based SQLite with
setup_db.py for fixture seeding, and add tests for auth guard, credentials
management, full registration flow, health endpoint, password auth, and
magic link registration errors. 66 checks across 7 test files.
Set up tests/e2e/ with a login page test covering branding, accessibility,
form structure, theme colors, and static asset serving. Includes run.sh
that manages the app lifecycle (start, test, stop) automatically.