Appearance
Password Management
Security protocols for managing user and client credentials, including real-time change validation and security notifications.
Change Password Flow
Users and Clients can update their current password. This process is secured by requiring the existing password and enforcing complexity/difference rules.
- Request Change: The authenticated subject provides
currentPasswordandnewPassword. - Validation:
- Schema level:
newPasswordMUST be different fromcurrentPassword. - Business level:
currentPasswordMUST match the stored hash.
- Schema level:
- Persistence: If valid, the new password is hashed (SHA-256) and the entity's
passwordHashis updated. - Notification: An integration event (
PasswordChangedIntegrationEvent) is published.
Sequence Diagram
sequenceDiagram
participant User as Authenticated Subject
participant API as User/Client Auth Controller
participant Bus as CommandBus
participant Handler as ChangePasswordHandler
participant Geo as Geolocation Service
participant DB as Identity Database
participant Pub as IdentityPublisher
User->>API: POST /auth/user/change-password {current, new}
API->>Bus: Execute ChangeUserPasswordCommand
Bus->>Handler: execute()
Handler->>DB: Fetch entity
Handler->>Handler: Verify currentPassword matches hash
Handler->>Geo: Resolve location via IP (IGeolocationPort)
Handler->>DB: Save entity with new hashed password
Handler->>Pub: publishPasswordChanged()
Pub-->>User: Security notification email (via EventBus)
Handler-->>API: Result.ok
API-->>User: 200 OK
Security Notifications
Every time a password is changed successfully, a notification email is sent to the account owner. This is a critical security measure to alert owners of unauthorized changes.
Email Content Includes:
- Device Info: Browser name and OS.
- IP Address: The origin of the change request.
- Geolocation: Physical location (resolved via Geolocation API) associated with the IP.
- Timestamp: Exact time of the modification.
- Next Steps: Advice on how to secure the account if the change was not authorized.
Scope
- Users: Fully supported through
UserAuthController. - Clients: Fully supported through
ClientAuthController.
Unlike new-device login notifications (which are Users-only), Password Change notifications are sent to both Users and Clients.
Set Initial Password (Google-only accounts)
Users that registered or signed in via Google have passwordHash = null and cannot use the change-password flow because there is no current password to verify. The Set Initial Password flow lets these users add an email + password method without losing their Google link.
Endpoint
POST /identity/auth/user/set-initial-password
- Body:
{ newPassword: string (min 8) }— validated bysetInitialPasswordUserSchema. - Auth: requires an authenticated user session (
@AuthUser()). - 2FA: protected by
@RequiresTwoFactor(TwoFactorChallengePurpose.CHANGE_PASSWORD)— the same challenge gate aschange-password. - Rate limit: 3 requests per 15 minutes per
userId(matcheschange-password).
Handler rules
SetInitialUserPasswordHandler enforces the operation's invariants:
- Returns
IDENTITY.PASSWORD_ALREADY_SET(409) when the user already has apasswordHash— those users must usechange-password. - Returns
IDENTITY.GOOGLE_NOT_LINKED(403) when the user has nogoogleId— set-initial-password is only valid for Google-linked accounts. - Returns
IDENTITY.USER_NOT_FOUND(404) when the user cannot be loaded. - Returns
IDENTITY.SESSION_NOT_FOUND(404) when the current session is missing from active sessions.
On success, the handler hashes the new password, persists it, revokes every other active session for the user, rotates the current session's tokens, and publishes a PasswordChangedIntegrationEvent (same notification path as the change flow).
Detecting eligibility from the panel
The panel reads two derived booleans from GET /identity/user/current (userResponseSchema):
hasPassword:truewhenpasswordHash !== null.googleLinked:truewhengoogleId !== null.
The panel's Security card switches into Set Initial Password mode when hasPassword === false && googleLinked === true. In that mode the form hides the Current Password field, shows an explanatory banner about the Google link, and renders a Confirm Password field to prevent typos that would lock the user out of password sign-in.
Raw fields
passwordHashandgoogleIdare NEVER exposed by the API. The mapper computes the boolean flags so the panel cannot accidentally rely on internal state.