Story: Implement the credentials journeys in ores.web
Table of Contents
This page documents a story in Sprint 26. It captures the goal, current status, acceptance criteria, and the tasks that compose it.
1. Goal
The three Credentials journeys run in
ores.web against the real server:
- Protect my account — the member changes their own password and reads where the account is signed in.
- Rescue access — the tenant administrator resets one colleague's password and locks or unlocks the account.
- Audit sign-ins — the tenant administrator reviews the active sessions, the session activity and the failed attempts of the tenant.
This is the fourth group of Implement the user journeys in ores.web. It reuses the journey runtime the Setup group landed and adds no journey entity. The journey documents stay with the extraction pass.
The group is prototype-first. Four steps the journeys need have no server operation today: self-service recovery, two-factor enrolment, ending one other session, and the authentication event log. Two more read subjects that exist and return no rows. The shape of the three screens is not settled either. A prototype settles the screen before the server work is sized, and it states each gap instead of hiding it.
The build tasks are written when the prototypes are accepted.
2. Status
| Field | Value |
|---|---|
| State | DONE |
| Parent sprint | Sprint 26 |
| Now | Nothing. |
| Waiting on | Nothing. |
| Next | Nothing. |
| Last touched | 2026-10-01 |
3. Acceptance
- Each Credentials journey has an accepted prototype on a
prototype/<journey>branch, with the variants it compared, the verdict and the rejected variants recorded on its task. - No prototype merges. The branches stay as the design record, and the accepted decisions are ported by the build.
- Every prototype states the gaps its journey document records. A panel with no server operation says so, and no screen claims a read or a write the server does not serve.
- Every claim in the three journey documents that the tree contradicts is recorded on this story, and is corrected in the document with the extraction pass or filed as a capture.
- The build tasks are written from the accepted prototypes, and each one names the server gap it depends on.
- Every journey runs in
ores.webagainst the real server, and the shell reaches the same subjects for the operations it carries today.
4. Tasks
| Task | State | Start | End | Description |
|---|---|---|---|---|
| Scaffold story: Implement the credentials journeys in ores.web | DONE | 2026-09-30 | Story scaffolding rides this task: the story, the prototype tasks, the sprint wiring and the scaffold PR. Close it before merging that PR. | |
| Prototype Protect my account | DONE | 2026-09-30 | [wayfinder:prototype] The member's screen: the password form, the sign-in state, the active sign-ins, and the gap where one other session cannot be ended. | |
| Prototype Rescue access | DONE | 2026-09-30 | [wayfinder:prototype] The administrator's screen: the account, its security state, the reset form, lock and unlock, and the gaps for activate and deactivate and for self-service recovery. | |
| Prototype Audit sign-ins | DONE | 2026-09-30 | [wayfinder:prototype] The event log: the filters, the active sessions, the session activity, the failed attempts, and Refresh as the only action. | |
| Correct the claims the credentials prototypes disproved | DONE | 2026-09-30 | [wayfinder:task] The three journey documents name the wrong surface, claim a working browser read and name a caller that does not exist. Correct them against the route census and the tree. | |
| Inventory the routes and operations the credentials journeys need | DONE | 2026-09-30 | [wayfinder:research] Per journey, every read and write, its subject, its ores.http route, its BFF route and its permission, so the build tasks can be sized from one page. | |
| Give the browser the session reads | DONE | 2026-10-01 | The audit screen reads sessions and no BFF route serves them. Add the page route and the active-sessions route over the subjects that already work, with the wire shapes and the route tests. | |
| Guard the session reads with the permission the catalogue grants | DONE | 2026-10-01 | The three session read handlers check no permission while the account and login-record reads check theirs, and the catalogue grants iam::sessions:read for exactly this purpose. Any authenticated caller can list a tenant's sessions today. | |
| Implement the active sessions read | DONE | 2026-10-01 | iam.v1.sessions.active replies success with no rows, so the audit screen's active list is empty by construction. Read the sessions whose end time is empty, guard the operation with the permission the catalogue grants, and test the read. | |
| Give the web client the credentials reads | DONE | 2026-10-01 | The BFF serves accounts, login records and sessions; the browser's own client cannot ask for them. Add the client methods the three screens call, each parsed at the network boundary with the schema the browser and the wire share. | |
| Build the member's Security screen | DONE | 2026-10-01 | Protect my account runs in ores.web on the accepted variant B: the places the account is signed in lead, and the password form and sign-in state share the row under them. | |
| Build the administrator's Rescue access screen | DONE | 2026-10-01 | 2026-10-01 | Rescue access runs in ores.web on the accepted variant B: the account's security state leads and names the action it suggests, and the recovery action and the lock control sit under it. Adds the two write routes the journey needs and states the recovery gap. |
| Build the sign-in audit screen | DONE | 2026-10-01 | 2026-10-01 | Audit sign-ins runs in ores.web on the accepted variant B: three readings behind tabs over one filter bar, over the session and login-record reads the group already landed, with every panel that has no producer saying so. |
5. Plan
The prototypes come first, because the screen decides what the server must serve. Each prototype leaves a branch a person can run and a verdict on the task.
| # | Step | What it leaves behind |
|---|---|---|
| 1 | Scaffold the story and wire it into the programme | The story, the four prototype tasks, the sprint row and the programme's order table. |
| 2 | Prototype Protect my account | The member's screen: the password form, the sign-in state, the active sign-ins, and the gap where one other session cannot be ended. |
| 3 | Prototype Rescue access | The administrator's screen: the account, its security state, the reset form, lock and unlock, and the gaps for activate and deactivate and for self-service recovery. |
| 4 | Prototype Audit sign-ins | The event log: the filters, the active sessions, the session activity, the failed attempts, and Refresh as the only action. |
| 5 | Write the build tasks from the accepted prototypes | One task per screen and one per server gap, each with the accepted prototype named as its design record. |
6. Decisions
- The accepted variants are B on all three screens. Protect my account leads with the places the account is signed in; Rescue access leads with the diagnosis the state suggests; Audit sign-ins puts its three readings behind tabs over one filter bar. Each prototype task's Result records what its accepted variant settles and what the recommendation gave up, so the rejected shapes are not lost.
- Rescue access sends a recovery link, and the administrator never chooses the password. The product owner raised this after the variant comparison, and it is the design. The system emails a single-use, time-limited link to the address on the account, and the colleague sets a password that only they know. Two consequences follow. An administrator-typed password survives only as a collapsed fallback for a mailbox that cannot receive, and whether it stays is the build task's first question. And the forced change at the next sign-in drops out of this journey, which retires its oldest gap: a service method with no caller.
- The administrator-typed fallback does not ship. Decided 2026-10-01, when the
builder answered the question the prototype raised. A control that has to
disclaim its own record — an administrator who types a colleague's password
knows that password — is worse than no control, and the product owner's
direction was the link. The
iam.v1.accounts.reset-passwordsubject therefore keeps no browser caller, the screen draws no password field, and Rescue access is corrected to match. Until the link machinery exists, the screen draws the action and states the gap. - The recovery link is new platform capability, not a credentials screen. Nothing in the tree sends mail, no subject requests a reset and no table holds a token. Filed as The tree has no way to send an email, and password recovery needs one, because sign-up verification and notifications will want the same machinery.
- Prototypes precede the build. A screen with four missing operations cannot be sized from its document alone. The prototype shows the shape, the copy and the empty states, and it makes the server work visible before anybody plans it.
- The group starts before Profile, on the prototypes. The programme's order puts Entry and Profile first. The prototypes here settle three screens and change no server code, so they collide with neither group. The build waits on the Entry group for the sign-in screen that owns the forced password change, and on the server gaps the prototypes record.
- The prototypes ride the shell and the shared primitives, not the step rail.
A credentials journey is one screen, so the ordered-step runtime the Setup
group landed does not apply:
projects/ores.web/packages/web/src/journeys/runtime.tsandJourneyPage.tsxrender a rail of steps, andparts.tsxis tenant provisioning. The screens composesrc/components/AppShell.tsxwithsrc/ui/Primitives.tsx,src/ui/PasswordField.tsxandsrc/ui/passwordPolicy.tsinstead, which is the material the build uses. - The prototype branches are throwaway and never merge. Each branch is
cut from
feature/implement-credentials-journeys, namedprototype/<journey>, and left unmerged. This follows the sprint 25 prototype story and the programme's decision that the prototypes are the design record and are ported, not merged. - A prototype task's branch field names its prototype branch, and its record rides this story's branch. A branch that never merges cannot carry a verdict, so every task's Result and state land here, and the branch field points at the code. Every other task's branch field points at this branch, because that is where both its work and its record are.
- The journey documents stay with the extraction pass, except for the claims
the prototypes disproved. Those are corrected in place and recorded here,
because a design document that names the wrong surface misleads the build.
Corrected 2026-09-30, in
Extract the user journeys that replace the ores.qt screens's
documents: the change-password verification note in Protect my account, the
sessions.activeread path in Protect my account and Audit sign-ins, and the reset-flag caller note in Rescue access. - The browser has no credentials path, and the routes that exist serve other
clients.
ores.httpserves the IAM subjects —/api/v1/iam/sessions,/sessions/active,/login_info,/accounts,/accounts/lock,/accounts/unlockand/accounts/reset-password— and each forwards to the matching subject. The BFF is the browser's only HTTP surface and serves none of them, so every credentials screen needs BFF routes before it can read or write anything.sessions/activeanswers with no rows on every path, and the samples, the statistics and the auth events have neither a subject nor a route. - The screen states the gap, and never simulates it. A panel whose subject returns no rows says the data is not available. A control the server cannot perform is drawn as unavailable with its reason. A prototype that fabricates the missing operation answers a different question than the one it was built for.
- The credentials screens are panels of one screen each. The journeys compose them that way, and the sprint 25 IAM exercise settled that an owned record is a panel inside a journey, not a screen of its own.
- Both clients reach one operation.
ores.webandores.shellcall the same subjects. Where the shell carries no credential command today, the gap is recorded for the build rather than closed here. - The prototypes follow the accepted contracts. The component specification, the UI design principles, the UX language and the TypeScript Clean Standard bind the work. The entity specification is void and binds no screen.
7. Out of scope
- The journey extraction and the journey documents, which Extract the user journeys that replace the ores.qt screens owns.
- The server operations the journeys need and do not have. The prototypes record them, and the build tasks size them.
- The Entry group's sign-in screen, which owns the forced password change and the recovery paths.
- The permanent homes of the account list and the account page. The journeys link to them, and the pages are their own work.
- Two-factor authentication as a feature. The gap is recorded, and the enrolment operation is not designed here.
8. Documents
| Document | Kind | Purpose |
|---|---|---|
| The routes and operations the credentials journeys need | investigation | What each step can reach today, per journey, and the ten BFF routes, two handlers, six subjects and one defect behind the gaps. |
| Credentials journeys | knowledge | The group page: three journeys, three actors, and the audit's screen rule. |
| Protect my account | user_journey | The member's own password change and sign-in state, and its five gaps. |
| Rescue access | user_journey | The administrator's reset, lock and unlock, and its four gaps. |
| Audit sign-ins | user_journey | The tenant-wide session and failure review, and its four gaps. |
| Implement the user journeys in ores.web | story | The programme this group belongs to, and the order it keeps. |
| Prototype ORE Studio bootstrap in ores.web | story | The prototype method this story follows: one branch per journey, a verdict per branch. |
| Journey execution | knowledge | The composition classes and the transaction boundary. |
| User Journey Extraction Standard | knowledge | The section contract every journey document carries. |
| TypeScript Clean Standard | knowledge | The standard the prototype and the build are held to. |
9. Result
Done 2026-10-01.
The three Credentials journeys run in ores.web against the real server, each on
the variant B its prototype was accepted for:
- Security (
/security) — the member reads where the account is signed in, changes their own password and reads the sign-in state. - Rescue access (
/rescue) — the administrator finds a colleague, reads the security state, and locks or unlocks the account through two routes this group added. The recovery link is drawn unavailable with its reason. - Audit: sign-ins (
/audit) — the administrator reads the active sessions, the session activity and the failed attempts behind one filter bar, with Refresh as the only action.
The group added nine BFF routes over subjects that already worked, finished the
active-sessions read in ores.iam, generated the guard on the three session
reads from the entity model, and closed one defect: the session reads checked no
permission while the account and login-record reads checked theirs.
Every screen states the operations it cannot reach. The recovery link, the self-service recovery path, activate or deactivate, ending another account's session, the session samples, the authentication events and the account filter are each named on the screen that wants them, and no screen draws an empty panel in place of a reading the server cannot serve.
Decisions taken during the build. Two are worth carrying forward: the administrator-typed password fallback does not ship, recorded above; and the recovery link is new platform capability rather than a credentials screen, so it is captured rather than built here.
What remains, and where it is recorded. The recovery link, its token store and
a mail path are
a capture. Nothing ends a session,
so every session reads as open —
a capture. The account filter on the
session read is a scope gap the audit screen states. No test exercises a guard's
refusal, because ores.iam has no harness for one, and that is recorded on the
guard task. The three screens carry no rendering test that drives a browser,
because the web package has no DOM harness; each renders from a fixture instead.
The screens' copy is not in the three locales yet.