Signup + First Use
From "I want to try FuzzyCode" to a parent actively using it with a child — what screens does a user actually see, in what order?
§1 The big idea the UI doesn't tell you
The URL /user_login is not the login page. It's one template (user_management.html, 3603 lines) that swaps between eight hidden panels via a client-side switchForm() helper. What looks to the user like six different screens — sign in, OTP, signup with password, signup OTP, password reset, enter code, set new password, confirmation, welcome — all share the same URL. Browser back / forward / refresh always return the Sign-In panel.
Sign In
- Email / Username / Child login handle
- Password
- Sign in button
- OAuth (hidden in HTML)
- Links: Sign Up · Reset · OTP
Enter OTP
- 6-digit code input
- Verify OTP · Resend · Back
Sign Up (password)
- Email · Password
- "I am 18+ and parent/guardian"
- Terms checkbox
- Turnstile widget
- Sign Up (disabled until valid)
- Link → "Sign Up Just Email"
Sign Up (OTP only)
- Terms + Turnstile
- Sign Up → emails OTP
Reset Password
- Email input
- Send Reset Code
Enter Reset Code
- Email display
- 6-digit code
- Verify · Resend · Back
Set New Password
- New password input
- Update Password
Check Your Email
- 6-digit confirmation input
- Resend email
Welcome
- Active child context banner
- Manage Children button
- Go to editor
switchForm() helper toggles display:none/block. Evidence: user_management.html:1435-1555./user_profile (complete profile) · /payment-required (consent + plan picker; this is also the COPPA direct notice surface) · /parents/children/manage (family dashboard). Also Stripe's /payment-success and /payment-cancelled (hosted by us) after Stripe's own interstitial.
§2 Parent signup — two UX paths
Signup forks based on the user's link click AND on a backend feature flag, with no visual signal of which path they're on.
Sign In
- Click link Sign Up
Sign Up
- password
- ☐ I am 18+ and parent/guardian
- [Turnstile widget]
- Sign Up
Branch point
authenticated=true→ go to profilepending_confirmation=true→ show #confirmation-container
Sign Up
- Sign Up Just Email
Sign Up (OTP)
- ☐ Terms · [Turnstile]
- Sign Up
create_user=true
Enter OTP
- 6-digit
- Verify
After authenticated signup — the real URL transitions
Welcome
- tokens issued
- auto-redirects
Complete Your Profile
- first name last name
- child name child birthday
- username
Parent Consent + Plan
- Yearly / Monthly
- COPPA direct-notice text (inline, not modal)
- Checkout → Stripe
✓ Success
- webhook records
parental_consent_date - auto-redirects to manage
Family Dashboard
- Household overview
- Add a child form
- Child cards
- "Manage Children" was the entry
§3 Parent adds a child
Every credential mutation requires a parent step-up modal — a password re-auth that mints a short-lived step-up cookie. This is enforced even for the parent who is logged in milliseconds ago.
Family Dashboard
- Add a child form:
- private_child_label
- public_username
- child_birthday
- ☐ confirm_private_use
- Add child
Family Dashboard
- new child card appears
- status: pending_private_use
- Login Management nav
Child Credentials
- Per-child card:
- login handle
- new password
- Save Revoke sessions
Confirm it's you
- parent password
- Verify
- POST /auth/session/step-up/password
scope=child_credentials_manage
Child Credentials
- Save ✓ saved
- Backend: creates Supabase alias
<handle>@email-alias.fuzzycode.dev
§4 Child first login (uses the same /user_login)
There is no child-specific login page. The field placeholder tells both parents and children to use the same input.
Sign In
- Parent Email, Parent Username, or Child Login Handle
- password
- Sign In
- ⚠ Reset / Sign Up / OTP links all visible to child — will all fail
Supabase edge fn
- edge fn
sign-intranslates handle → alias email - broker cookies issued
- COPPA gates skipped (parent already consented)
FuzzyCode editor
- index.html
- no context banner for child
§5 Password reset — parent only
Four panels of the same shell, one URL, with a strict enforcement that children cannot enter this flow.
Sign In
- Reset Password
Reset Password
- Send Reset Code
- rejects @email-alias.fuzzycode.dev
→ Supabase /auth/v1/recover
Reset code email
- 6-digit code
- from Supabase
Enter Reset Code
- 6-digit
- Verify
type=recovery
Set New Password
- new password
- Update Password
- rejects actor_type=="child"
/parents/children/credentials with step-up.
§6 Account recovery (lost email / locked account)
Beyond password reset, recovery is an out-of-band operator process. The self-service surface is a contact form.
Parent Contact
- request type ▾
- parent email / name
- child selector / reference
- message
- Submit
contact/requests
Admin queue
- admin runs playbook
- verifies identity
- approves / rejects
Resolution email
- reset link / account unlocked / etc.
There is no SMS recovery, no security questions, no recovery codes, and no "trusted device" recovery. Full inventory of what exists: this form + admin playbook.
§7 Parent ↔ child context switch
Two identical-looking switch UIs coexist on the same page and no in-editor switcher exists. A parent who wants to "use as child" must navigate to Family Dashboard, click, then navigate back to the editor.
Family Dashboard
- Selection panel:
- child-context-select ▾
- Use selected child (dropdown path)
- -- or --
- Per-card Use this child (card path)
- Both call
selectChildContext(id)
child-context
Family Dashboard
- (no page reload)
- text in #selection-summary updates
- ⚠ no toast; no nav; easy to miss
- parent step-up cookie cleared
FuzzyCode editor
- new
fc_parent_child_ctxcookie applied - ⚠ no context banner
- parent sees child's drafts (IndexedDB partitioned per-child)
§8 Gotchas the code revealed
user_management.html:664-672 is commented out with a warning not to re-enable. The routes /auth/session/oauth/start + callback are fully wired. A future re-enable needs UAT verification per the comment.
#auth-container is shown; links don't filter based on input. A child who types their handle and clicks "Reset Password" will fail with an obscure internal error.
broker_bootstrap_flows_enabled() — a feature flag the user can't see. Two different UX paths for the same action.
/user_profile child fallthrough. A child actor without a name lands on the parent's "Complete Your Profile" page (user_profile.html:222-264) showing parent-only fields. Narrow edge but surfaced by the code.
#selection-summary changes. Users can easily assume the click did nothing.
/user_login with no ?next= preservation.
§9 Verification pointers
- Single auth template
FuzzyCode/templates/user_management.html(3603 lines) - Panel switching
user_management.html:1435-1555(switchForm(), handlers) - /user_login render
auth.py:1331-1346 - /signup route
auth.py:5510 - Broker bootstrap flag
auth.py:5604-5622(broker_bootstrap_flows_enabled) - /payment-required
auth.py:1378-1424· templatepayment_required.html:196-308 - /user_profile
auth.py:3081-3104· templateuser_profile.html:222-264 - /parents/children/manage
auth.py:1783-1796· templateparent_children_manage.html:480-650 - /parents/children/credentials
auth.py:1769-1780· templateparent_child_credentials.html:170-200, 402-426 - Parent step-up modal
FuzzyCode/static/fuzzy-blocks/parent_step_up_modal.js· endpointauth.py:3764 - Child password update block
auth.py:3650-3657(CHILD_PASSWORD_PARENT_MANAGED) - Child alias email block
auth.py:3584-3587 - Add child API
auth.py:1828· servicechild_private_use.py - Child credentials set API
auth.py:2040 - Context switch endpoint
auth.py:3230· template handlersparent_children_manage.html:1359-1641 - Password reset flow
auth.py:3570, 3479, 3627 - Account recovery
auth.py:1427-1439, 1500· playbooktemplates/admin/parent_rights_verification_playbook.html - Child alias email naming
child_credentials.py:416-417 - OAuth dead in UI alive in server
user_management.html:664-672(commented) ·auth.py:3949, 4011(routes)