Proto Account Self-Heal Findings
Purpose
This note captures the current findings from the proto-account discussion so we can refer back to them before making any self-healing changes.
Scope:
- consumer SSO login behavior
- missing
profile - missing
user_email - missing or broken
user_identity - pending-account and recovery side effects
No code changes are proposed in this document.
Summary
- Missing
profilelooks safe to heal. - Missing
user_emailis mostly non-blocking for login, but it matters for recovery and email-based account flows. - Missing or broken
user_identityis the dangerous case. - The current pending-account flow can activate a local user based on email confirmation without also proving that the delegated identity link is healthy.
Current Login Path
SSO login uses the submitted email as the username
In public_html/process-login.php, the login flow sets:
$username = $email
It then looks up the principal before asking api.user for a root session:
- consumer mode:
platform\package\SSO\m7\db\User::get - tenant mode:
platform\package\SSO\m7\db\Client::get
Relevant path:
Full record lookup does not include email rows
The shared registry lookup loads:
useridentityprofile
It does not load user_email as part of the login lookup shape.
Relevant path:
Missing identity is not fully rejected at the first SSO lookup for active users
platform\package\SSO\m7\db\User::get() requests a verified identity row, but it does not explicitly reject a missing identity object if the user row exists and the account is active.
Relevant path:
For pending users, SSO throws PendingAccountException.
Relevant path:
api.user root-session does require a linked identity
The consumer root-session endpoint loads the user by name and then calls validateUserActive().
That validator explicitly rejects a missing identity:
Linked identity not found
Relevant paths:
- /Users/hr/personal/code/frontend/api.user.m7.org/lib/php/thisProject/api/auth/user/RootSession.php
- /Users/hr/personal/code/frontend/api.user.m7.org/lib/php/thisProject/api/Controller.php
So for active consumer users, a missing local identity row usually fails downstream in api.user, even if the first SSO lookup succeeds.
Missing Profile
Current behavior
Missing profile does not appear to block login.
api.user auth paths read profile data only opportunistically, such as profile.avatar.
Relevant path:
Existing self-heal behavior
/me/set-profile already creates the profile row if it is missing.
Relevant path:
Recommendation
This is the safest self-heal candidate.
Creating a minimal profile on login or first authenticated account load should be low risk.
Missing Email
Current behavior
Missing user_email does not appear to block the basic login path, because login does not authenticate through the email row itself.
However, email rows matter for:
- account confirmation
- forgot-login
- forgot-password
- primary-email selection and account UX
Existing tolerant behavior
/me/get-email already returns an empty-ready response when no email row exists:
no email found. ready for input
Relevant path:
Existing recovery / pending behavior
The pending-account flow can create or restore an email row for the user:
- restore archived row if present
- otherwise insert a new email row
- mark it unverified
- archive competing email rows
Relevant path:
Recovery depends on verified email
Forgot-login only resolves by a verified active email row.
Relevant path:
Forgot-password also filters to verified email rows when building local recovery methods.
Relevant path:
Recommendation
Missing email is partly handled already.
Good rule of thumb:
- do not block login on missing email if identity is healthy
- do allow post-login setup or repair
- keep recovery flows dependent on verified email
Missing Or Broken Identity
Current behavior
This is the highest-risk case.
If the local user_identity row is missing, api.user consumer auth rejects the login because validateUserActive() requires a linked identity.
Relevant path:
If the local identity row exists but the remote delegated child is broken upstream, there does not appear to be any self-heal path today. That likely fails later in the remote signer / password validation path.
Important nuance: unverified identity is not the same as missing identity
Consumer signup currently creates:
- local user
- remote child
- remote password
- local identity row
But the local identity row is inserted with verified = 0.
Relevant paths:
- /Users/hr/personal/code/frontend/api.user.m7.org/lib/php/thisProject/api/auth/Signup.php
- /Users/hr/personal/code/php/m7-php-platform/package/auth/user/registry/service/Admin.php
For consumer auth, the practical hard requirement in api.user is that an identity object exists, not that it is marked verified.
So the truly dangerous state is:
- no identity row
- wrong
rid - remote child deleted or unusable
not merely verified = 0.
Pending-Account Risk
Email can be attached during pending repair
The pending-account send operation accepts a submitted email, prepares a local email row for the pending user, and requests confirmation.
Relevant path:
Email confirmation can activate the local user
After successful confirmation, the pending-account verify operation:
- marks the local email verified
- sets it primary
- updates local user status to
active
Relevant path:
This activation step currently does not also prove that the delegated identity link exists and is healthy.
Why this matters
This means the system can move a local user from pending to active through email proof alone, even though login still ultimately depends on the delegated identity state.
That is the main place where the current flow feels too permissive relative to the danger of missing identity.
âAccept Any Usernameâ Concern
What the code suggests today:
- it is not âany username on earthâ
- it is closer to âan existing local pending account can have a new email attached and confirmedâ
That is still risky enough to deserve tightening, especially if the accountâs delegated identity is missing or broken.
The handoff data for pending accounts even preserves identity_rid when available:
That makes identity-aware repair possible later, but it is not enforced today in the activation step.
Recommended Guardrails
Safe to auto-heal
- missing
profile
Probably safe to keep non-blocking
- missing
user_emailduring login, as long as identity is healthy
Should not be silently auto-healed during normal login
- missing or broken
user_identity
For identity repair, the safer shape would be:
- require a pre-existing trusted anchor such as a verified email already attached to that uid
- require an explicit repair flow
- verify the remote child can be created or re-linked correctly
- only then allow activation or normal login
Best Future Hardening Target
If we do one surgical hardening pass later, the highest-value place is probably the pending-account activation flow.
Specifically:
- do not promote the local user to
activebased only on email proof if the delegated identity is absent or broken - route that case into an explicit identity-repair step instead
That would keep the friendly recovery behavior for missing email and missing profile, while avoiding magic recovery for the most security-sensitive state.