Authentication in ClinicFlow is handled entirely in the application layer. Commands validate credentials, enforce lockout rules, and manage tokens — but JWT issuance is deliberately not part of this layer.Documentation 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.
LoginUserCommand returns only the authenticated user’s Guid; the presentation layer (e.g., an API controller or minimal-API endpoint) is responsible for generating and signing the JWT using that Id. This separation keeps the core logic free of infrastructure concerns.
Phone verification (
SendPhoneVerificationCommand + VerifyPhoneCommand) must be completed before a patient can schedule appointments. The IsPhoneVerified flag on UserDto reflects the current state; the ScheduleByPatient handler checks it at scheduling time.LoginUserCommand
Authenticates a user by email and password, enforces lockout rules, and returns the user’s Id for JWT issuance. Returns:IRequest<Guid> — the authenticated user’s Id.
Registered email address. Must be a valid email format and within
EmailAddress.MaximumLength.Plain-text password. The handler verifies it against the stored hash via
IPasswordHasherService.- Looks up the user by email via
IUserRepository.GetByEmailAsync. ThrowsBusinessRuleValidationException(DomainErrors.User.InvalidCredentials) if not found. - Calls
IPasswordHasherService.Verify(password, user.PasswordHash)to validate the credential. - Invokes
UserAuthenticationService.TryAuthenticate(user, isValid, utcNow):- On success: calls
user.RecordLogin(loginTime)— clears failed attempts and lockout, updatesLastLoginAt. Returnstrue. - On failure: calls
user.RecordFailedLogin(referenceTime)— incrementsFailedLoginAttempts; if>= 5, setsLockoutEnd = now + 15 minutes. Returnsfalse.
- On success: calls
- Saves changes and then throws
BusinessRuleValidationException(DomainErrors.User.InvalidCredentials) ifTryAuthenticatereturnedfalse, so the lockout update is still persisted.
Guid Id. The presentation layer should use this Id to look up roles and issue a signed JWT.
LogoutUserCommand
Revokes all refresh tokens for a user, effectively ending their session. Returns:IRequest (no return value)
The Id of the authenticated user to log out. Must be a non-empty Guid.
IRefreshTokenService.RevokeAsync(userId). This revokes all active refresh tokens for the user. Short-lived access tokens remain valid until their natural expiry — enforce short TTLs (e.g., 15 minutes) at the presentation layer.
Phone Verification Commands
SendPhoneVerificationCommand
Dispatches an OTP code to the user’s registered phone number via IPhoneVerificationService.
Returns: IRequest (no return value)
Id of the user whose phone number should receive the OTP. Throws
EntityNotFoundException if not found.IPhoneVerificationService.SendVerificationCodeAsync(user.PhoneNumber). The service implementation (infrastructure) is responsible for rate-limiting and delivery.
VerifyPhoneCommand
Validates the OTP entered by the user and marks the phone number as verified.
Returns: IRequest (no return value)
Id of the user attempting verification.
The OTP code received by the user.
- Loads the user or throws
EntityNotFoundException. - Calls
IPhoneVerificationService.VerifyCodeAsync(user.PhoneNumber, code)— returnsbool. - Calls
user.MarkPhoneAsVerified(isValid):- Throws
DomainValidationException(DomainErrors.User.InvalidVerificationCode) ifisValidisfalse. - Throws
DomainValidationException(DomainErrors.User.PhoneAlreadyVerified) if the phone was already verified. - Sets
IsPhoneVerified = trueon success.
- Throws
- Persists via
IUnitOfWork.
ChangePasswordCommand
Allows an authenticated user to change their own password by supplying the current password for verification. Returns:IRequest (no return value)
Id of the authenticated user.
The user’s existing password in plain text for verification.
The new password to set. The validator enforces minimum length of 8 characters.
- Fetches the user or throws
EntityNotFoundException. - Verifies
CurrentPasswordagainstuser.PasswordHashviaIPasswordHasherService.Verify. ThrowsBusinessRuleValidationException(DomainErrors.User.InvalidCredentials) on mismatch. - Hashes
NewPasswordand callsuser.ChangePassword(newHash), which also resetsFailedLoginAttempts = 0and clearsLockoutEnd. - Persists via
IUnitOfWork.
RequestPasswordResetCommand
Initiates the forgot-password flow by generating a signed reset token and sending it to the user’s email address. Returns:IRequest (no return value)
The email address associated with the account. Must be a valid email format.
- Looks up the user by email.
- If the email does not exist, the handler silently returns without error. This prevents email enumeration attacks.
- If found, calls
IPasswordResetTokenService.GenerateTokenAsync(userId)to produce a time-limited token. - Calls
IEmailService.SendPasswordResetEmailAsync(email, token)to deliver the reset link.
ResetPasswordCommand
Completes the password reset flow by validating the token and setting a new password. Returns:IRequest (no return value)
The opaque reset token delivered via email. The service validates its signature and expiry.
The new password to set. Minimum 8 characters enforced by the validator.
- Calls
IPasswordResetTokenService.ValidateTokenAsync(token)— returns the associateduserId?. ThrowsDomainValidationException(DomainErrors.Validation.InvalidValue) if the token is invalid or expired. - Fetches the user by
userIdor throwsEntityNotFoundException. - Hashes
NewPasswordand callsuser.ChangePassword(newHash), resettingFailedLoginAttemptsandLockoutEnd. - Persists via
IUnitOfWork.
Authentication Flow Summary
Full login → JWT issuance sequence
Full login → JWT issuance sequence
Guid, loads any additional claims (role, etc.), and calls its JWT provider.