31 KiB
P7-01 — Authentication and account-security presentation
Status: implemented P7-01 scope CONDITIONAL GO; public8ad720d and accountad7b419 normally pushed, final static PASS and actual direct receipts recorded. This is the bounded execution/acceptance plan from the approved handoff. P7-00 27c54146ad6f9059295d169b533db7b1b6689210 is pushed and direct development/localHEAD/origin equality verified at2026-10-11 14:44:45UTC; main unchanged and worktree clean at entry. Its baseline and ledger satisfy entry. No new routes, identity/provider protocols or capability activation are included. Public8ad720da5db5999ecda787575dc45621d18a5498 is normally pushed; localHEAD=origin=live development directly verified2026-10-11 18:50:24UTC, local/origin/live main27657f3 unchanged. No completed public work remains uncommitted. The separate account implementation/runtime/visual outcome is CONDITIONAL GO below; final staged finite hygiene passed and normal account pushad7b419 has actual direct receipt2026-10-11 19:16:15UTC. The public sub-slice alone does not complete P7-01.
Baseline and bounded commit scope
The inherited accepted runtime baseline is backend1,033, frontend521/85, ordinary browser159, sidecar49 and separate existing security/HTTP/fidelity lanes. P7-00 performed fresh identity/routes/matrix/static checks only. New affected runtime evidence begins here; every count delta must identify actual added/removed cases and avoid adding focused subsets to full totals.
| Coherent commit boundary within P7-01 | Existing source owners | Allowed presentation scope / exclusions |
|---|---|---|
| Public authentication | Login/Register, Forgot, Reset, Verify/email-change, Microsoft relink | Reuse existing shared page/forms/state/error patterns; coherent headings/names/labels/focus, responsive layout and translated feedback. Preserve safe from/query/hash/state, remember-me, registration/verification/token/Turnstile/2FA and provider exchange semantics |
| Account identity/security | Profile, AuthStatus, 2FA, Sessions | Shared theme/section hierarchy, honest status/error/retry, dialog-local failure, locale dates and long metadata reflow. Preserve identity-only profile payload/local preference ownership, avatar/crop/remove, email confirmation/password, recovery/trusted-device/current-session/revoke semantics |
| Shared provider consumers affected by either boundary | Google, Microsoft | Real consumers are LoginPage sign-in presentation and ProfilePage account-link presentation. Prove both after shared changes; preserve link password, popup origin/state, cancellation/no-exchange, safe return and unlink guards. Mailbox grants remain separate |
The exact checkpoint remains P7-01; these are reviewable commit boundaries, not additional phases. Account changes follow the public-auth commit to avoid conflicting shared-provider edits. Keep existing save/fetch/security owners; no dead-code cleanup or backend concurrency/provider redesign hidden in presentation.
Read-only source findings to reproduce before repair
| Observation at entry | Bounded correction candidate / required proof |
|---|---|
| TwoFactorSettingsCard status GET catches failure into null; null then selects Enable2FA action as though disabled | Distinct pending/known/failed status and deliberate retry; no misleading enabled/disabled assertion or setup action while unknown. Request counts, known enabled/disabled branch and failure→retry readback tests |
| Sessions revoke-others failure sets the global error rendered outside the still-open confirmation dialog | Durable alert inside that dialog, retained confirmation/retry/cancel and focus; no background-isolation workaround or automatic resend/revoke |
| AuthStatus signed-in sentence is a literal English sentence; account/security/provider dates use host-default locale | Existing i18n interpolation/natural EN/NB wording and explicit UI-locale date formatting; test actual two locales with fixed timestamps and shared provider presentations |
| Account child card headings default from typography variant; shell already owns page h1 and Profile has explicit contextual headings | Use existing section primitives and explicit contextual levels/names; prove loaded full hierarchy and preserve field/action names. Do not add a second h1/main |
| Profile load/avatar/remove/save/password errors still use raw response/message stringification, while email-change paths use shared helper | Reuse getApiErrorMessage with translated caller fallback and structured server explanations where reproduced; retained draft/local state and deliberate successful recovery. Do not change payload/save semantics |
| Trusted-device/session metadata uses absolute secondary-action list layout | Verify long device names/dates at320/390 and keyboard/zoom before any reflow repair; source layout alone is not measured overflow evidence |
Installed MUI Dialog generates aria-labelledby and DialogTitle inherits its ID. Missing explicit ID props alone do not establish an unnamed-dialog defect; actual named-dialog/description/focus/error tests determine whether a repair is required. Preserve recovery confirmation before dismissing codes, password/code clearing and no automatic sensitive action on entry.
Required automated and native evidence
| Scope | Existing anchors / additions needed |
|---|---|
| Local auth and safe navigation | Login tests, Verify tests, native auth return/expiry. Exercise login/register/reset/verify/relink loaded/error/expired/used-token states, remember-me, recovery/rate-limit and unsafe/nested return rejection without route/protocol changes |
| Account identity versus Career | Profile tests, native account isolation. Add identity-only save payload/readback, no canonical Career/CV/sibling write, local-only vs external read-only rules, pending email request/cancel/password failure/retry and owner-switch disposal |
| 2FA status/confirm/recovery/devices | 2FA tests currently cover enable→recovery and wrong-password disable. Add unknown/error/retry, known status, recovery acknowledgment/clear/cancel, trusted-device error/revoke and actual named modal trap/return; no real authenticator qualification claim |
| Sessions and sign-out | Add focused real Sessions/AuthStatus tests for load failure/retry, remote vs current revoke, canceled revoke-others/no-write, failed confirm within modal and deliberate retry/readback. Preserve clearAuthClientState/private-tree disposal with session-boundary tests |
| Shared providers | Current Login tests exercise real Google/Microsoft components under mocked external exchange; Profile tests mock Google. Add real account-link/unlink presentation/negative-path coverage so Login success cannot stand in for Profile compatibility. No actual provider call without separate authority |
| Responsive/localised/accessibility | EN/NB, light/dark,320/360–390/768/1024/1280/1440/wide representative states, long labels/device names, actual relevant zoom, keyboard/focus/errors/loading/retry and dialog state. Capture DOM names, root overflow, screenshot and axe contexts; use current theme/page/i18n owners |
Use owned synthetic fixtures and actual mutation/no-write/readback assertions. Run appropriate affected unit/native tests, app/browser TypeScript, lint/build and finite docs/secret/whitespace checks. A broader full runtime gate is required at P7-12; extra runs here need a new change/failure/unresolved concern. Preserve ordinary P2-D01, existing auth/tenant/AI/frozen/output guards and original PDF oracles.
Acceptance and remaining gates
- Public/auth and account commits have bounded diffs, real consumer regression, exact commands/results/count deltas, source identity and retained intermediate failures. Commit/push each completed unit and record immediate direct remote=HEAD in the ledger.
- Existing32 routes/aliases/search/history/state remain; no weakened authentication/authorization, canonical mutation, duplicate provider exchange/save owner or silent sensitive-action semantics change.
- EN/NB/theme/responsive names/headings/error/retry/focus behavior is verified in actual representative native states, with axe incomplete/violation contexts retained rather than disabled.
- Applicable E01–E06/E09 and shared E07/E08 plus S03 boundaries remain in the 57 matrix. Finite scoped repairs do not upgrade whole groups without missing mandatory evidence.
- Actual mail/OAuth/Turnstile/authenticator/device/reader results and M01–M12 remain explicit when unavailable. M13/P5-13 remains deferred do-not-execute. Keep all debt, production NO-GO and main unchanged; no Phase8 customization.
Public authentication implementation and acceptance
The public evidence manifest records the fresh final commands, source/export identity, all 61 affected unit assertions, 22 native assertions, two isolated SMTP workflows, 22 all-enabled axe attachments, exact geometry, image hashes and retained intermediate attempts. Public UI runtime files are copied to the immutable .artifacts/phase7/p7-01/public-final/ snapshot before account runs reuse the runner lanes; affected-unit/backend artifacts retain their unique original paths. Only manifest file references were rebased: raw reports retain original absolute execution/attachment paths internally, and commands, times, results and measured SHA256 values are unchanged. Portable PNG references resolve to the snapshot. The six public source files use UTF-8/LF normalization so their hashes remain stable when Git stages LF. The manifest is written with LF; no commit hash is inferred from its own contents.
The five existing view owners now use AuthPageFrame, the existing PageHeader/theme spacing and a bounded 520px form width. Each public page owns one named main landmark/H1. Recovery forms have durable page feedback, labelled local validation, focus on the failed field, retained input and explicit resubmission. Verification/email-change loading and success have named dynamic status; invalid/missing token pages retain a usable recovery action. Long action labels wrap at narrow widths. Existing route, command, token, Turnstile, remember-me, provider and safe-return owners remain in place.
Visual review of the first passing export found registration describing its password as “Current password.” Two ordinary password-intent unit cases reproduced the mismatch; registration now uses the existing “New password”/“Nytt passord” key while preserving new-password autocomplete. The final export and native/recovery runs below include that repair. No new translation key or hard-coded product string was required.
| Fresh bounded evidence | Observed final result | Scope / count explanation |
|---|---|---|
| Affected public Jest files | 61/61, four suites, zero failure/pending | Existing Login33 and Verify4 retained; Microsoft relink1→2; new public-presentation22. Compared with the inherited affected38 cases, +23 cases/+1 suite. This is not a fresh full frontend run |
| Ordinary public native pack | 22/22, zero skip/flaky/unexpected/retry | Fifteen finite presentation samples, two keyboard recovery/prefill/validation workflows, three real invalid-token HTTP400 workflows and two failed-login→explicit-success/return-context workflows. Fresh2026-10-11 18:40:40–18:41:10UTC |
| Isolated actual Identity + loopback SMTP pack | 2/2, zero skip/flaky/unexpected/retry | Missing-token/no-write plus real registration202, delivered verification link204, invalid link400, password-reset delivery/reset204/replay400, old password/session rejection and protected return. Fresh18:41:10–18:41:23UTC; separate from the22 ordinary cases |
| Loopback SMTP fixture helper | 6/6, zero fail/cancel/skip/todo | Fragmented commands/dot stuffing, envelope reset, oversize rejection, quoted-printable UTF-8, folded base64 and unsupported-MIME handling. Separate tooling cases |
| Backend auth/security subset | 87/87, zero failed/not-executed | Auth/session/local identity/context/2FA/email-ownership/authorization contracts at unchanged backend source; a subset of inherited1,033, not an additive total |
| App and browser TypeScript, lint, production frontend build | All exit0 | Final sequence18:40:04–18:40:39UTC; lint retains zero-warning policy. Browser config contents and command/log hashes are portable in the manifest |
Authoritative public build ID is rd_SQeVLwVwER9VMCc6XL, with BUILD_ID bytes retained in the immutable public snapshot. Canonical public six-file source digest is b330b233508ad5202a1f622ade4464103f7a986ee53648c8f16047f2a1d32586; the manifest records each file and its precise ordered digest algorithm. Static export74 files have measured raw-file digest dff031a52740a09359715a31456c9cec70f5aae6f9932c018af078215e973d67. These identify the tested public source subsequently pushed in8ad720d, not a deployment or the later account build. The manifest preserves entry observation27c5414 and adds the actual successor push receipt separately.
Responsive, localisation and visual evidence
The native pack actually covers EN/NB and light/dark across320,360,390,768,1024,1280,1440 and1920px; the 1280×320 registration sample exercises short landscape height. It includes ready, registration-disabled, missing-link, local-validation, actual HTTP400 and actual HTTP401 states. Each of22 scans records one H1, named main and exact visible-control geometry; root/main overflow is0 throughout and visible controls remain within viewport tolerance. Public forms have no density control, so Comfortable/Compact is explicitly not applicable here. This is a finite sampling plan, not every route×theme×language×breakpoint combination, physical orientation or browser zoom acceptance.
The parent opened the eight exact final presentation-matrix PNGs with view_image: login NB/dark320; registration NB/light390 and NB/dark1280×320; forgot EN/dark360; reset EN/light320; verify NB/dark390; email-change EN/dark1024; Microsoft relink EN/dark1440. Final registration images visibly say “Nytt passord.” The reviewed samples have calm shared framing and reachable/wrapped labels/actions, with no new observed app-chrome overflow. The manifest records those eight exact raw PNG hashes. Other workflow screenshots were captured and hashed; individual visual inspection is not claimed for them.
The22 all-enabled axe scans contain 0 violation nodes and31 incomplete nodes, all color-contrast. Every retained check says the background could not be determined because another element overlaps it. Contexts include outlined empty/filled form inputs and two primary-button nodes in failed-login states. Exact targets, HTML, check IDs/messages/data, context and geometry are retained in the manifest; complete raw attachments remain embedded in the hashed native report. Reflow and zero reported violations do not prove contrast. M11 must qualify contextual contrast/forced colors; M09 must qualify spoken names/errors/state and possible duplicate route/error announcements. Browser zoom, human screen reader and physical/software-keyboard qualification remain unverified. No axe tag/rule was suppressed to obtain these results.
Frontend-owned copy uses the existing EN/NB keys. Actual invalid-token responses include existing server-authored English “Invalid or expired link.”/“Invalid email or token.” in NB samples; this is retained as a localisation qualification, not presented as translated server feedback. Actual external OAuth, Turnstile and production email delivery remain unqualified; the SMTP run uses the real application mail/Identity path against an owned local capture server, not an external service.
Retained failures and preservation
The first SMTP attempt passed0/2 because strict alert selectors also matched the established route announcer. The second passed1/2 because the fixture expected direct login where the protected-route handoff actually opens the existing Landing page. Scoped-main alert assertions and following that actual Landing handoff fixed the test assumptions; no product change was made for these failures. The third passed2/2 before the label repair, and the fresh final run passed2/2 after it. Their reports/logs/command records and whole output folders are retained under .artifacts/phase7/p7-01/public-final/attempts/; the manifest resolves archived attachment paths to their retained folders rather than later output files.
Intermediate public unit attempts are also retained, including the59-case run with one failure, the later59/59 run, and the password-label diagnostic RED with1 pass/1 failure/20 unselected pending cases. The final61-case affected run has zero pending/failure; diagnostic testNamePattern selection is not used for final acceptance. The first passing22-case public native run remains historical evidence and did not excuse the visually identified label defect.
The finite public checks preserve E01–E06/E09 and E07/E08/S03 boundaries, exact safe return/query/hash handling, real token failure/replay and old-session rejection. Backend routes/protocols and established link/exchange guards are retained. All 57 groups remain56 PARTIAL + C11 DEFERRED, and all32 routes remain retained with their existing qualifications. No whole-group upgrade or fresh full frontend/backend/browser/fidelity acceptance is inferred from these subsets. Ordinary P2-D01 remains required by the inherited baseline and future integrated gate; it was not rerun in this public pack.
Public sub-slice acceptance is CONDITIONAL GO for pushed8ad720d, with M01–M12, server-error language/reader/contrast/provider qualifications and inherited debt carried. Production remains NO-GO, main unchanged; M13/P5-13 remains deferred do-not-execute. The ledger and manifest contain the actual normal push/direct remote receipt; entry/public source and runtime history remain separate from account work.
The public static record checked2026-10-11 18:48:17UTC records secret scan and working/staged/Phase7 whitespace commands all exit0, all57 statuses and32 routes preserved. An earlier checker parsed56 rows because it did not accept bold DEFERRED; accepting optional Markdown emphasis repaired the checker, with no matrix/status change. Later account/successor documentation requires its own fresh finite hygiene run.
At the public gate, five account-presentation RED cases and12 real-provider cases were prepared; those historical counts remain in the frozen public manifest. Subsequent account results are recorded separately below. Focused provider sets overlap existing public cases and cannot be added to a full-suite result. The public slice does not change the approved checkpoint order.
Account identity and security implementation
Implemented account scope: CONDITIONAL GO after fresh runtime and actual final visual/context review; final staged finite hygiene passed; accountad7b419 normally pushed with actual direct receipt19:16:15UTC. The account manifest records the actual fresh runtime results below, exact seven-file source/export identity, all native attachment bodies and geometry, intermediate failures and case-name deltas. Its public entry reference preserves the already pushed public slice. The whole final UI runtime tree including attempts, focused unit records, BUILD_ID, runner/browser config, inherited frontend baseline, seven source snapshots and exact export are frozen under .artifacts/phase7/p7-01/account-final/. All206 recorded path/hash references were verified; only file references were rebased, preserving raw bytes, hashes, commands and original report execution paths. Source identity retains original logical source paths with separate immutable snapshots. Do not recollect this slice after subsequent builds. No backend source, authentication protocol, schema, provider capability or external service was changed.
AuthStatus, 2FA, Sessions and the two real account-provider consumers use the existing themed PageSection with named H2 sections. The shell owns H1; Profile's existing identity/account/password sections keep their save boundaries. Signed-in wording and security/provider dates follow the selected EN/NB locale. Long identity/session metadata wraps within the existing responsive layout.
Unknown or failed 2FA status announces loading/failure and offers a deliberate read-only retry; it cannot suggest setup until an actual status response is known. Sessions loading failure no longer looks like a known empty list. A failed revoke-others request keeps its durable alert inside the named confirmation and preserves explicit retry/cancel. Pending confirmation blocks duplicate commands; current-session logout still clears the authenticated private tree. Existing enable/disable, recovery acknowledgement/export/clear, device revoke, password and provider-link/unlink owners remain in place.
Profile uses the existing API-error helper and durable scoped save/password feedback, retaining the draft after failure for explicit retry. Identity save still sends exactly firstName/lastName/displayName/userName; account email, canonical Career data and CV variants retain their separate owners. Native fixtures read canonical data and two sibling CVs before/after an actual identity PUT204 and reload, and assert no browser canonical/CV write.
Visual inspection of the first passing account export exposed two copy defects: a disabled Microsoft account card instructed the product user to set a NEXT_PUBLIC environment variable, and the account chip called a CV missing even when canonical Career data and two CV variants existed. The chip's actual source is only stored profileCvText. Two ordinary EN/NB Profile cases and a strengthened existing provider assertion reproduced these failures (33 pass/3 fail, no selected-out cases). Account-only profileCvTextAvailable/Missing keys now describe that stored text truthfully; the existing Microsoft unavailable hint uses natural EN/NB wording. Existing Career CV keys/data, CV-library fetching and provider configuration semantics are unchanged.
Fresh account regression and source identity
| Evidence | Actual result | Scope / count explanation |
|---|---|---|
| Full frontend Jest | 573/573 in89 suites, zero fail/pending/todo | Fresh19:03:13–19:06:30UTC. Inherited521/85 becomes+52 cases/+4 suites: public23, account providers12, card presentation7, security flows5 and Profile5 (19→24). No removed/renamed cases; exact names are retained in the manifest |
| Account native | 6/6, zero skip/flaky/unexpected/retry | Fresh19:04:29–19:05:01UTC. Four actual identity-save/reload/isolation samples, failed-2FA-read→explicit-retry/no-write, and real multiple-session cancel/failure/retry/readback/current-logout workflow |
| Public native regression | 22/22, zero skip/flaky/unexpected/retry | Fresh19:05:01–19:05:33UTC on the account export; separate from the immutable public acceptance run and its SMTP evidence |
| Existing auth/account-isolation browser boundaries | 10/10, zero skip/flaky/unexpected/retry | Fresh19:05:33–19:06:16UTC on the same account export. Current/private-tree disposal, canonical account separation, safe return and expired authentication retain their ordinary assertions |
| App/browser TypeScript, lint, frontend production build | All exit0 | Fresh19:03:46–19:04:29UTC, lint zero-warning policy retained. Earlier unknown-response spread in a new test was corrected with a test-only structural assertion; the diagnostic failed typecheck is retained |
The measured account export is SbTVDn-CSb21uPKSzFqOv. Seven product owners have canonical-LF digest 91589b37e8efe9e0a089987158af44e5f846d0b56cc78782423f38508404f915; the74 exported files have raw digest 04926e1ee0c75884a8a61b70b3b65e158ad237d65c2714e196ace6d692655bda. The manifest records per-file hashes and the ordered digest algorithms. These identify the tested working tree after the19:02:57UTC product freeze, before the account commit, with entry HEAD8ad720d. The final test-only structural assertion does not change product source. Backend1,033, sidecar49, ordinary integrated159 and rendering corpora remain inherited; this account slice does not claim fresh executions of them or ordinary P2-D01.
Account accessibility and responsive evidence
Eight all-enabled account snapshots cover NB/dark320, EN/light390, NB/light768 and EN/dark1440 identity persistence, plus unknown/recovered 2FA and failed/settled session revocation at320/390. All eight record root overflow0 and eight named sections within the viewport. Actual identity fields save/reload, long names and security dates are included. The six account cases do not vary density; both Comfortable/Compact are not claimed. These finite contexts do not establish every breakpoint/theme/language combination, physical touch/software keyboards, browser zoom or a human screen-reader result.
Account scans retain 0 violation nodes and85 incomplete nodes:84 color-contrast (74 background overlap,8 short-text,2 partial overlap) and1 aria-hidden-focus. The failed revoke-others modal targets #root with focusable-modal-open: “Check that focusable elements are not tabbable in the current state.” The ordinary native test proves named dialog/description, installed MUI initial focus container, subsequent forward/backward keyboard trap cycle, cancel focus restoration, failure retained inside the modal and deliberate retry. That bounded keyboard evidence does not replace M09 spoken navigation/state or M11 contextual contrast/forced-colors qualification. Exact nodes/check messages/data and source contexts remain in the manifest and hashed raw report; no rule was disabled.
The public regression adds22 separate all-enabled scans with0 violation nodes/31 contrast incompletes. Combined30 account-build snapshots have0 violation nodes/116 incomplete occurrences, but the8 account and22 public contexts remain separate; they are not like-for-like substitutes for Phase6's mixed-scope319 snapshots. The earlier account export had84 incompletes; the final failed-modal snapshot has one additional overlap node. No incomplete was hidden to preserve an earlier count.
Eight PNGs are extracted byte-for-byte from the inline base64 report attachments into a report-SHA directory; their exact raw hash and original embedded-report reference are recorded. The parent actually opened all eight final post-copy PNGs with view_image: four persisted identity samples, failed/recovered2FA read, failed revoke dialog and surviving-session view. Stored-text wording is truthful, the unavailable Microsoft hint contains no operator variable, and controls/layout remain clear and in bounds. Sticky appbar/skip-link positions in stitched full-page captures reflect their actual scroll/focus capture context. Earlier inspected identity images and their two copy defects remain historical. The manifest contains exact image hashes and the direct review receipt; extraction performs no editing and visual review does not provide human assistive acceptance.
Historical failures and remaining acceptance
The first native account attempt passed1/6. Four cases assumed Profile PUT returned200, whereas the established AuthController contract returns204. Another assertion expected initial focus inside the Paper role=dialog, although installed MUI FocusTrap initially focuses its containing dialog container. Fixtures now require204 and the actual initial trap container; subsequent Tab/Shift+Tab still must cycle inside the named dialog and cancel restores the invoking control. This corrected test assumption did not alter product authentication/save semantics or relax keyboard containment. Its whole report/log/output folder is retained.
The provider RED8 pass/4 fail, original card RED0/5, strengthened card RED1/7, focused GREEN sets and copy RED33/36 are retained with their exact counts. Earlier full571/89 and passing account/public/boundary native runs precede the copy repair and remain historical, not final acceptance. The fresh full573 run has no pending/skipped/expected-failure masking. Whole previous runtime folders are retained by the runner; raw reports still contain their original execution paths.
All57 groups remain56 PARTIAL + C11 DEFERRED, all32 routes retained with existing qualifications. This slice adds finite E02–E06/E08/E09 and C01/C07/S03 evidence; it does not upgrade whole groups. Human reader/zoom/physical and software-keyboard, real OAuth/Turnstile/authenticator/device and production delivery gates remain open. Existing server-authored English errors in NB remain an explicit localisation qualification. M01–M12 and inherited debt carry forward; M13/P5-13 stays deferred do-not-execute, Phase8 stays separate, production NO-GO and main unchanged. Implemented account and whole-P7-01 scope are CONDITIONAL GO with these explicit qualifications. Final staged finite hygiene passed; accountad7b419 was normally committed/pushed, localHEAD=origin=live development directly verified2026-10-11 19:16:15UTC. Local/origin/live main27657f3 remains unchanged, worktree clean at immediate observation. Runtime/source/public evidence stays frozen and the actual successor receipt is appended separately. The latest account static record uses static-checks.json; its final2026-10-11 19:15:39UTC metadata and canonical-LF JSON hash are recorded under the actual push receipt.
The final account static checks passed2026-10-11 19:15:38UTC: tracked/untracked secret scan, working/staged/Phase7 whitespace,303 relative documentation links,57 groups and32 original routes. Root independently verified all206 account manifest file/hash references. Normal account pushad7b419 was directly verified2026-10-11 19:16:15UTC; this successor appends that actual receipt without changing frozen measurements.