Force password change (temporary passwords)
Make an admin-set temporary password genuinely temporary — the user can do exactly one thing with it: set a new one.
When an admin sets a temporary password for a member, the temp credential must not grant ongoing access — the only thing it should allow is setting a new password. The mustChangePassword flag does that, with two layers:
- Surfaced flag (always on) — the user's
mustChangePasswordis returned on the login response and on/auth/me, so the UI can route straight to the change-password screen. - Hard-block guard (opt-in) — when enabled, the API itself rejects everything except the change-password flow until the password is rotated. The API is the real gate; the UI redirect is just convenience (and a direct API call can't bypass it).
1. Enable enforcement
2. Set the flag when an admin assigns a temp password
Set it only on the admin-sets-another-user's-password path — not on self-service set-password or a member's own change (they pick their own there), and not on invites (the member sets their own).
3. What the guard allows while the flag is set
With enforce: true, a flagged user gets 403 MUST_CHANGE_PASSWORD on every guarded route except the allowlist needed to finish signing in and rotate the password:
change-password,logout- the current-user / session endpoints (
/auth/me,/auth/user,verify-session) refresh-token- the MFA and email/phone verification routes (so a 2FA user can complete sign-in)
Exempt your own routes that must stay reachable with @SkipMustChangePassword():
4. It clears itself
A successful change-password (or set-a-new-password via reset-password) clears mustChangePassword automatically — the user resumes normal access.
Client
Multi-account apps
With allowMultipleAccounts: true you sign in through addAccount(), not login(). The snapshot it resolves to carries the same flag, so the prompt works without an extra /auth/me call:
completeMfa(...) / commitAccount(...) report it too, so an MFA-gated sign-in is covered.
It is one-shot, on purpose.
mustChangePasswordappears only on the snapshot returned by the sign-in call — never onlistAccounts()snapshots, and it is not persisted. A cachedtruewould survive the password change and bounce the user back to the change-password screen forever. To re-check later, read/auth/me(client.getSessionUserData()), which is always current.undefinedmeans "not observed here", not "false" — withenforce: truethe guard is still the real enforcement, so a missed redirect degrades to a server-side block, never to open access.
Related
- Invite a member.
- Multi-account login & switching — the
addAccountsign-in path. - Hooks reference.
- Error codes.