Skip to content

Pending-claim recovery

This repository-only MVP is non-gating for isolated synthetic rehearsal and gating before real-user migration (#2922, parent #2466). It does not authorize deployment, user communications, migration, or changes to live mappings.

Boundary

The canonical schema's Identity-only identityFeatureFlags group emits SYRF__FeatureFlags__IdentityClaimRecovery from featureFlags.identityClaimRecovery, default false. Identity binds the FeatureFlags section once at startup into its own IdentityFeatureFlags type (IdentityHostOptions.FeatureFlags), not the shared-kernel FeatureFlags class. The entry carries no featureFlag: true annotation or web: block, so it is absent from the API runtime flag catalogue and browser flags: an admin runtime toggle could never reach Identity. Generated Helm supports Identity; the bulk chart-defaults updater currently does not, so Identity's chart explicitly declares the same false default.

With the flag off, recovery GET/POST handlers return 404, the admission middleware does not exempt /Account/ClaimPending (so an incomplete account is still sent to its admission step), and authorization retains its existing unmapped-account refusal. With it on, an interactive authorization request for an authenticated, otherwise-ready unmapped user opens /Account/ClaimPending. Silent prompt=none, token, and userinfo refusal behavior is unchanged. Existing password-reset, email-confirmation, and profile gates still apply; the recovery page cannot bypass them.

The initial redirect follows the existing login/admission return-URL convention: GET query parameters or POST form authorization parameters (including OAuth state) appear in the local return URL. Protection of the retry continuation starts on the recovery page, not before this redirect; this slice does not claim to hide the original authorization request from browser history. Clients must not place credentials or personal data in OAuth state.

User and support path

Pending and rejected/unmapped accounts receive identical guidance: contact the existing helpdesk with the displayed random support reference, or check again after support confirms readiness. No account/investigator identifier, email, candidate information, credential, or rejection reason appears in the page.

Each page response records an outcome and random support reference. Authorized operators can correlate that reference in Identity logs; the account identifier is attached only using the existing PiiUserId scope/redaction convention. This reference is not a credential or authorization to edit a mapping. The MVP supplies no operator mapping-edit endpoint and sends no automated helpdesk communication.

Both handlers load the account through the same security-stamp validation as authorization: a stale cookie (for example after a password change) is signed out and challenged before any continuation is issued. A GET for an account that is already mapped resumes the original authorization request immediately. A redirect to an unmet admission step also records an AdmissionRequired outcome and support reference.

Retry is authenticated and antiforgery-protected (one token, emitted by the form tag helper). It validates the existing 15-minute, account-and-security-stamp-bound protected local continuation, and reads the stored SyrfUserId. An absent mapping only renews the guidance. A present mapping resumes the original local authorization request; only that existing authoritative flow can grant admission. Retry never links, creates, infers, or changes any mapping, and never issues tokens or refreshes a sign-in cookie. Expired, altered, or wrong-account continuations require starting sign-in again. External and protocol-relative return URLs fall back to /.

Verification and follow-ups

Focused tests cover pending/rejected guidance, repeated read-only retries, resolved continuation, wrong account/stamp, expiry/tampering, external return URLs, other admission requirements, flag-off behavior, silent authorization, privacy-safe audit events, real Razor rendering, and HTTP antiforgery enforcement. GET and form-POST authorization tests cover enabled, disabled and silent flows; POST uses the form's state rather than a competing query-string value. The HTTP harness uses synthetic users and no live database or credentials.

Operator reconciliation tooling, detailed claim-state UX, and real-user rollout remain separate work. Do not enable this flag as a substitute for verified immutable mapping or for the other migration acceptance gates.