User Journey: Protect my account

Table of Contents

This page documents a user journey: A member changes their password and sees where their account is signed in.

A user journey describes what one person is trying to do, before anything is built. It is a design document that outlives any sprint, so it names the screens, the entity fields each screen shows, and the operations and messages each step needs.

1. Goal

"I want to keep my account safe, and know where it is signed in." The member changes their own password, then reads the state of the account: the last sign-in, the failed attempts, whether the account is locked, and whether a password change is required. The journey is done when the new password works and the sign-in panel reads as the member expects.

2. Actor and trigger

The member: an account of type user, signed in, acting on their own record. The trigger is a password prompt, a security reminder, or an unfamiliar entry in the sign-in panel. It starts when the member opens Security from the account menu, and ends when they leave it.

This is the self-service half of the credentials topic. An administrator who must restore somebody else's access runs Rescue access instead, and the record of who signed in is Audit sign-ins.

3. Steps

  1. Open Security. The member chooses Security from the account menu. The system shows one screen: the password panel, the sign-in state, and the places the account is signed in.
  2. Change the password. The member enters the current password, the new password twice, and reads the strength hint. The system checks the new password against the policy, rejects a password equal to the current one, stores the new hash, and clears the password-reset flag.
  3. Read the sign-in state. The member reads the last sign-in, the failed attempt count, the lock state and the reset flag. The panel is read-only, because the service writes that state as a side effect of signing in.
  4. See where the account is signed in. The member reads the active sign-ins: client, address, country and start time. The system shows one row for each session that has no end time.
  5. Sign out. The member ends the current session with Sign out. There is no control to end one other session, so a member who sees an unfamiliar sign-in can only change the password and sign out.
  6. Leave. The member goes back. The account menu shows the account as it is.

4. Screens and wireframes

One screen. The password form, the sign-in state and the active sign-ins are panels of that screen rather than separate screens, because the member thinks of them as one thing.

wireframe_protect_my_account.png

Figure 1: Protect my account: the password form, the sign-in state, and the places the account is signed in.

5. Prototype

Accepted by the product owner on 2026-09-30: variant B, sessions first. The prototype is merged into main and kept there as the record; the branch names the accepted state.

Accepted commit
c4d2435c23, the tip of prototype/protect-my-account, merged into main
Accepted variant
b
Source
doc/prototypes/credentials/protect-my-account/index.html
Open
/OreStudio/doc/prototypes/credentials/protect-my-account/ once the site is built, or the file directly
Design record
Task: Prototype Protect my account

6. Entities composed

Entity What it contributes Model
account Username and account type; the row the password change writes ores.iam.account
login_info Last sign-in, failed attempts, locked, online, reset required, last addresses ores.iam.login_info
session Client, address, country, start time and end time for each sign-in ores.iam.session

Three account columns must never reach this screen: password_hash, password_salt and totp_secret. The password panel writes a hash; it never reads one back.

7. Operations and messages

Step Operation Subject Status
Open Security Read my account iam.v1.accounts.get exists
Read the sign-in state Read my login record iam.v1.login_info.get exists
Change the password Set a new password for myself iam.v1.ops.change_password exists
See where I am signed in List my active sessions iam.v1.ops.get_active_sessions partial
Sign out End my own session iam.v1.ops.logout exists
End one other sign-in End a chosen session none missing
Turn on two-factor authentication Enrol a TOTP secret none missing
See statistics over time Read the session statistics none missing

iam.v1.ops.change_password takes the account from the caller's token, so the member needs no administrator permission. The subject is declared in ores.iam.account_messages. The request carries current_password, and the service verifies it against the stored hash before it writes anything (account_operations_service::change_password), so the screen may say the server checks the field.

iam.v1.ops.get_active_sessions is partial: the subject exists in ores.iam.session_messages, and the handler replies with success and returns no sessions. ores.http exposes it as POST /api/v1/iam/sessions/active, which forwards to the same subject and so returns the same empty answer. The browser reaches it on no path: the BFF is the browser's only HTTP surface, and it serves no session route.

8. What is missing

  1. No way to end one session. iam.v1.ops.logout ends only the caller's own session. A member who sees an unfamiliar sign-in cannot end it. The candidate is iam.v1.sessions.end, restricted to the caller's own sessions.
  2. No TOTP enrolment. account.totp_secret exists as a column, and iam.v1.accounts.put accepts a secret when the account is created, but nothing enrols, replaces or clears a secret for an existing account. The candidate is iam.v1.accounts.enrol-totp, plus a read that states whether two-factor authentication is on. The secret itself never reaches the screen.
  3. iam.v1.ops.get_active_sessions returns nothing. The subject exists and is a stub, and the ores.http route forwards to it, so no path reads the session table for the member's own sign-ins. Until one does, the screen states that the active sign-ins are not available.
  4. No session statistics. ores_iam_session_stats_daily_vw and its hourly and aggregate siblings exist as TimescaleDB continuous aggregates, with no subject. The candidate is iam.v1.sessions.statistics.

9. Related journeys

  • Sign in — where this password is used, and where a forced change happens
  • Rescue access — the administrator's route back in when the account is locked and the member cannot sign in
  • Audit sign-ins — the tenant-wide view of the sessions this screen shows for the member alone

10. Related knowledge

11. See also

Emacs 29.3 (Org mode 9.6.15)