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. 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.

6. 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.accounts.change-password exists
See where I am signed in List my active sessions iam.v1.sessions.active partial
Sign out End my own session iam.v1.auth.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.accounts.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, but the handler reads only new_password. The screen may ask for the current password, and must not claim the server proves it.

iam.v1.sessions.active is partial: the subject exists in ores.iam.session_messages, and the handler replies with success, but it returns no sessions. The BFF answers /api/v1/sessions/active from the session repository, so the browser has a working read today and the NATS path is a stub.

7. What is missing

  1. No way to end one session. iam.v1.auth.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.save 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.sessions.active returns nothing. The subject exists and is a stub. Until it reads the table, the screen must call the BFF route.
  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.
  5. The current password is not verified. The change-password handler ignores current_password. A stolen token is enough to set a new password. The screen should treat the field as a confirmation, and the server should verify it.

8. 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

9. Related knowledge

10. See also

Emacs 29.3 (Org mode 9.6.15)