mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-10-15 18:14:44 +00:00
112 lines
3.9 KiB
Plaintext
112 lines
3.9 KiB
Plaintext
@startuml update-password
|
|
!include ../../../pipeline/diagrams/biergarten-weizen-theme.puml
|
|
skinparam sequenceMessageAlign center
|
|
skinparam maxMessageSize 200
|
|
|
|
title User Authentication Flow - Update Password
|
|
|
|
actor User
|
|
participant "API\nController" as API
|
|
participant "MediatR pipeline\n(ValidationBehavior)" as Mediator
|
|
box "Handler Layer" #F1F3EA
|
|
participant "UpdatePasswordHandler" as UpdatePasswordHandler
|
|
participant "UserManager<ApplicationUser>" as UserMgr
|
|
end box
|
|
box "Infrastructure Layer" #EBECE3
|
|
participant "Argon2PasswordHasher" as Argon2
|
|
end box
|
|
box "Repository / Cross-slice" #DBEEDD
|
|
participant "DapperUserStore\n(IUserStore/IUserPasswordStore/\nIUserEmailStore)" as UserStore
|
|
end box
|
|
database "SQL Server" as DB
|
|
|
|
note over UserMgr
|
|
ASP.NET Core Identity's UserManager<TUser>
|
|
replaces the deleted AuthRepository: handlers
|
|
no longer issue SQL or call Argon2 directly,
|
|
they delegate to UserManager, which in turn
|
|
calls DapperUserStore (persistence) and
|
|
Argon2PasswordHasher (IPasswordHasher<TUser>).
|
|
end note
|
|
|
|
User -> API: PATCH /api/auth/password\n{currentPassword, newPassword}\n(requires Bearer access token)
|
|
activate API
|
|
|
|
API -> Mediator: Send(UpdatePasswordCommand(\n GetAuthenticatedUserId(),\n currentPassword, newPassword))
|
|
activate Mediator
|
|
|
|
note right of Mediator
|
|
ValidationBehavior runs UpdatePasswordValidator
|
|
(same password strength rules as registration)
|
|
end note
|
|
|
|
alt Validation fails
|
|
Mediator -> API: throws FluentValidation.ValidationException
|
|
API -> User: 400 Bad Request
|
|
else Validation succeeds
|
|
|
|
Mediator -> UpdatePasswordHandler: Handle(command)
|
|
activate UpdatePasswordHandler
|
|
|
|
UpdatePasswordHandler -> UserMgr: FindByIdAsync(userAccountId)
|
|
activate UserMgr
|
|
UserMgr -> UserStore: FindByIdAsync(userAccountId)
|
|
UserStore --> UserMgr: ApplicationUser or null
|
|
UserMgr --> UpdatePasswordHandler: ApplicationUser or null
|
|
deactivate UserMgr
|
|
|
|
alt Account not found
|
|
UpdatePasswordHandler -> Mediator: throw NotFoundException
|
|
Mediator -> API: propagate
|
|
API -> User: 404 Not Found
|
|
else Account found
|
|
|
|
UpdatePasswordHandler -> UserMgr: ChangePasswordAsync(\n user, currentPassword, newPassword)
|
|
activate UserMgr
|
|
UserMgr -> Argon2: VerifyHashedPassword(\n user, user.PasswordHash, currentPassword)
|
|
activate Argon2
|
|
Argon2 --> UserMgr: Success/Failed
|
|
deactivate Argon2
|
|
|
|
alt Current password mismatch
|
|
UserMgr --> UpdatePasswordHandler: IdentityResult.Failed\n(PasswordMismatch)
|
|
else Current password matches
|
|
UserMgr -> Argon2: HashPassword(user, newPassword)
|
|
activate Argon2
|
|
Argon2 --> UserMgr: new hash
|
|
deactivate Argon2
|
|
UserMgr -> UserStore: SetPasswordHashAsync(user, newHash)
|
|
activate UserStore
|
|
UserStore -> DB: UPDATE dbo.UserCredential\nSET IsRevoked = 1, RevokedAt = GETDATE()\nWHERE UserAccountId = @Id AND IsRevoked = 0
|
|
UserStore -> DB: INSERT INTO dbo.UserCredential\n(UserAccountId, Hash)
|
|
note right of UserStore
|
|
Revoke-then-insert runs inside one
|
|
DbTransaction (credential rotation).
|
|
end note
|
|
UserStore --> UserMgr: (void)
|
|
deactivate UserStore
|
|
UserMgr --> UpdatePasswordHandler: IdentityResult.Success
|
|
end
|
|
deactivate UserMgr
|
|
|
|
alt Current password is incorrect
|
|
UpdatePasswordHandler -> Mediator: throw UnauthorizedException\n"Current password is incorrect"
|
|
Mediator -> API: propagate
|
|
API -> User: 401 Unauthorized
|
|
else !result.Succeeded (other failure)
|
|
UpdatePasswordHandler -> Mediator: throw ConflictException
|
|
Mediator -> API: propagate
|
|
API -> User: 409 Conflict
|
|
else result.Succeeded
|
|
UpdatePasswordHandler --> Mediator: UpdatePasswordPayload(\n user.Id, DateTime.UtcNow)
|
|
deactivate UpdatePasswordHandler
|
|
Mediator --> API: UpdatePasswordPayload
|
|
deactivate Mediator
|
|
API -> User: 200 OK\n{message: "Password updated successfully.",\npayload: {...}}
|
|
end
|
|
end
|
|
end
|
|
deactivate API
|
|
|
|
@enduml
|