Accounts and Child Access

At the source level, account credentials establish a session role; setting up a child credential adds a separate check that the child record belongs to the authenticated user. A role alone does not establish ownership.

flowchart TD
    register["Registration"] --> parentCredential["Parent credential"]
    parentCredential --> login
    login["Login"] --> session["Session record and cookie"]
    session --> resolve["Session resolution"]
    resolve --> identity["Session role and child ID"]
    session --> logout["Logout"]
    logout --> clear["Revoke session and clear cookie"]
    identity --> adult["Parent or admin session"]
    adult --> childSetup["Child-account setup"]
    childSetup --> ownership["Owned-child lookup"]
    ownership --> childCredential["Update or create child credential"]

[!NOTE] Registration and login call ensureDemoAccounts before processing submitted input. Its source inserts demo parent/admin users and a demo child, then calls a credential helper for parent, child, and admin roles; the helper returns if that normalized username already exists. This does not establish that demo identities are available in a deployment. Check the target environment’s provisioning and credential policy before relying on demo access.

[!NOTE] AdultAccessDock declares a link to /care/parent, but the connection from that link to a registered route and sign-in submission is not established here. Verify the route and form-to-handler connection before describing the link as an authenticated entry flow.

Account lifecycle

[
    {
        "title": "Registration",
        "body": "In `app/api/auth/register/route.ts`, the POST handler normalizes the username and requires a display name and a 4–64 character username matching the lowercase letters, digits, period, underscore, and hyphen pattern. The password must be at least 10 characters and contain a letter and a digit. An existing username is rejected. Otherwise, the handler inserts a parent-role user, then an active credential with a salted password hash, and returns 201 with a sign-in prompt."
    },
    {
        "title": "Login",
        "body": "In `app/api/auth/login/route.ts`, empty username or password is rejected before the credential lookup. The `account_login` limiter allows up to 8 attempts per 300-second fixed window; its key combines the `cf-connecting-ip` header value—or the `browser` fallback—with the normalized username. The helper catches failures in its rate-limit work and returns `allowed: true`, so treat the threshold as best-effort and verify the datastore failure path before relying on it. Login then requires an active credential with a matching password and a corresponding user record. It creates a session, updates the credential’s last-login time, and returns a `redirectTo` value for the child, admin, or parent role (`/child`, `/care/control`, or `/care/parent/growth`) along with the session cookie."
    },
    {
        "title": "Session resolution",
        "body": "The shared resolver in `app/api/_lib/auth.ts` matches the cookie token’s hash to an unexpired session joined to an active credential and user. For child-role sessions, resolution returns null if the credential has no child ID or the request path and method fail the child-request allowlist. The session handler in `app/api/auth/session/route.ts` returns selected identity, role, and child-ID fields when a user resolves; otherwise, it returns an authentication-required response. The cookie is HttpOnly, Secure, and SameSite=Lax, with a seven-day maximum age."
    },
    {
        "title": "Logout",
        "body": "The handler in `app/api/auth/logout/route.ts` calls the revocation helper and returns a cookie-clearing value. When the request contains a session token, the helper deletes the matching session record; without a token, it skips deletion and the handler still clears the cookie."
    }
]

The ChatGPT sign-in return-path helpers in app/chatgpt-auth.ts are separate from this Mogo account-cookie session flow.

Child credentials

The handler in app/api/auth/child-account/route.ts resolves the session before allowing only parent or admin roles to continue. After parsing the request, it checks that the supplied child ID identifies a record whose parentUserId matches the authenticated user ID; findOwnedChild applies both conditions in app/api/_lib/ownership.ts. This ownership check is distinct from the role gate, so an admin or parent session alone does not authorize setup for a child record owned by someone else.

After ownership is established, the handler requires a 4–64 character normalized username matching the same pattern used for registration and a password of at least 8 characters. It rejects an active username already used by another credential. If the child already has a credential, it updates that record; otherwise, it inserts a child-role credential. The response uses status 200 for an update and 201 for a creation.