Every principal in ClinicFlow — whether a patient booking appointments or an administrator managing the clinic — is modelled as aDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/0Crazy-0/ClinicFlow/llms.txt
Use this file to discover all available pages before exploring further.
User entity. The domain handles password hashing, tracks failed login attempts, enforces a 15-minute lockout after five consecutive failures, and provides a complete password reset flow using a secure token delivered by email.
The User Entity
| Field | Type | Default | Notes |
|---|---|---|---|
Email | EmailAddress | — | Value object; unique per user |
PasswordHash | string | — | Stored hash; never the raw password |
PhoneNumber | PhoneNumber | — | Value object; used for SMS verification |
Role | UserRole | set at registration | Immutable after creation |
IsActive | bool | true | Set to false by DeactivateUser |
IsPhoneVerified | bool | false | Flipped by VerifyPhone |
FailedLoginAttempts | int | 0 | Incremented on each failed login |
LockoutEnd | DateTime? | null | Set to now + 15 min after 5 failures |
LastLoginAt | DateTime? | null | Updated on every successful login |
UserRole Enum
Account Lockout
User.RecordFailedLogin increments FailedLoginAttempts. When the count reaches MaxFailedLoginAttempts (5), LockoutEnd is set to the current UTC time plus LockoutDuration (15 minutes).
User.RecordLogin resets both FailedLoginAttempts and LockoutEnd to their initial values on a successful authentication. Calling ChangePassword also clears the lockout, so a successful password reset always unblocks a locked-out account.
Registration Commands
ClinicFlow exposes four distinct registration commands, one per role. All share the same fields but are isolated so that role-specific authorization policies can be applied at the presentation layer.RegisterUser
Self-registration for patients.Assigns
UserRole.Patient. Checks for duplicate email before creating the user.RegisterDoctorUser
Creates a user account for a physician.Assigns
UserRole.Doctor. After registration, create a linked Doctor profile with CreateDoctorProfile.RegisterReceptionistUser
Registers a front-desk receptionist.Assigns
UserRole.Receptionist.RegisterAdminUser
Registers a system administrator.Assigns
UserRole.Admin. Should be protected by an existing admin authorization policy in the presentation layer.IPasswordHasherService.Hash before calling User.Create, so the plaintext password is never stored.
Authentication
LoginUser
Guid — the authenticated User.Id.
The handler delegates to UserAuthenticationService.TryAuthenticate, which calls either User.RecordLogin or User.RecordFailedLogin based on the result of IPasswordHasherService.Verify. The FailedLoginAttempts counter (and any lockout) is persisted immediately via SaveChangesAsync even when authentication fails — ensuring the lockout state is durable across restarts.
LoginUser returns only the user’s Guid. JWT generation, access token signing, and refresh token issuance are not part of the Application layer — they are expected to be handled by the presentation (API) layer after a successful login. The IRefreshTokenService interface (see below) is provided so the application can revoke tokens when needed (e.g., on logout or password change), without the domain depending on JWT infrastructure.IRefreshTokenService
LogoutUser uses this interface to invalidate the user’s session.
Phone Verification
Phone verification is a two-step process orchestrated throughIPhoneVerificationService — a domain interface (in ClinicFlow.Domain.Interfaces.Services) implemented in the Infrastructure layer.
Send verification code
Dispatches an SMS to the phone number on file.The handler loads the user, reads
User.PhoneNumber, and calls IPhoneVerificationService.SendVerificationCodeAsync(user.PhoneNumber, cancellationToken).Verify code
Submits the code received via SMS.The handler calls
IPhoneVerificationService.VerifyCodeAsync(user.PhoneNumber, code, cancellationToken), passes the boolean result into User.MarkPhoneAsVerified(isVerificationCodeValid), and saves. Throws InvalidVerificationCode if the code is wrong and PhoneAlreadyVerified if the number is already verified.IPhoneVerificationService
Password Management
ChangePassword
Requires knowledge of the current password. On success, clearsFailedLoginAttempts and LockoutEnd.
User.ChangePassword(newHash). Submitting an incorrect CurrentPassword throws InvalidCredentials.
Password Reset Flow
The reset flow is designed to prevent email enumeration:RequestPasswordReset silently succeeds even when the email does not exist.
RequestPasswordReset
Generates a secure time-limited token and emails it to the user.Handler: loads user by email (returns early if not found), calls
IPasswordResetTokenService.GenerateTokenAsync, then IEmailService.SendPasswordResetEmailAsync.Supporting Interfaces
Deactivation and Reactivation
DeactivateUser sets IsActive = false; ReactivateUser sets it back to true and resets FailedLoginAttempts and LockoutEnd. A deactivated user cannot log in — User.RecordLogin throws AccountInactive regardless of the password.
ICurrentUserService
The presentation layer must provide an implementation ofICurrentUserService so that application-layer handlers can resolve the identity of the caller without depending on HTTP context directly.
Queries
GetUsers
Paginated, filterable list of all users — intended for admin dashboards.SearchTerm is matched against Email and PhoneNumber. All filter parameters are optional.
GetLockedOutUsers
Returns all users whoseLockoutEnd is in the future — useful for support staff monitoring repeated login failures.
UserDto
LockoutEnd is null for users who are not currently locked out. A non-null value greater than the current UTC time means the account is actively locked.