mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-10-15 18:14:44 +00:00
110 lines
3.4 KiB
Plaintext
110 lines
3.4 KiB
Plaintext
@startuml confirm-user
|
|
!include ../../../pipeline/diagrams/biergarten-weizen-theme.puml
|
|
skinparam sequenceMessageAlign center
|
|
skinparam maxMessageSize 200
|
|
|
|
title User Authentication Flow - Confirm User
|
|
|
|
actor User
|
|
participant "API\nController" as API
|
|
participant "MediatR pipeline\n(ValidationBehavior)" as Mediator
|
|
box "Handler Layer" #F1F3EA
|
|
participant "ConfirmUserHandler" as ConfirmHandler
|
|
participant "UserManager<ApplicationUser>" as UserMgr
|
|
participant "TokenService" as TokenSvc
|
|
end box
|
|
box "Infrastructure Layer" #EBECE3
|
|
participant "JWT\nInfrastructure" as JWT
|
|
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: POST /api/auth/confirm?token=<token>\n(requires Bearer access token)
|
|
activate API
|
|
|
|
API -> Mediator: Send(ConfirmUserCommand(token))
|
|
activate Mediator
|
|
|
|
Mediator -> ConfirmHandler: Handle(command)
|
|
activate ConfirmHandler
|
|
|
|
ConfirmHandler -> TokenSvc: ValidateConfirmationTokenAsync(token)
|
|
activate TokenSvc
|
|
TokenSvc -> JWT: Validate against confirmation\ntoken secret, extract userId
|
|
activate JWT
|
|
JWT --> TokenSvc: ValidatedToken or throws
|
|
deactivate JWT
|
|
TokenSvc --> ConfirmHandler: ValidatedToken
|
|
deactivate TokenSvc
|
|
|
|
alt Token invalid or expired
|
|
ConfirmHandler -> Mediator: throw UnauthorizedException
|
|
Mediator -> API: propagate
|
|
API -> User: 401 Unauthorized
|
|
else Token valid
|
|
|
|
ConfirmHandler -> UserMgr: FindByIdAsync(validatedToken.UserId)
|
|
activate UserMgr
|
|
UserMgr -> UserStore: FindByIdAsync(userId)
|
|
activate UserStore
|
|
UserStore -> DB: SELECT ...\nFROM dbo.UserAccount\nWHERE UserAccountID = @Id
|
|
activate DB
|
|
DB --> UserStore: row or null
|
|
deactivate DB
|
|
UserStore --> UserMgr: ApplicationUser or null
|
|
deactivate UserStore
|
|
UserMgr --> ConfirmHandler: ApplicationUser or null
|
|
deactivate UserMgr
|
|
|
|
alt User not found
|
|
ConfirmHandler -> Mediator: throw UnauthorizedException\n"User account not found"
|
|
Mediator -> API: propagate
|
|
API -> User: 401 Unauthorized
|
|
else User found
|
|
|
|
ConfirmHandler -> UserStore: SetEmailConfirmedAsync(user, true)
|
|
note right of UserStore
|
|
IUserEmailStore<ApplicationUser> is injected
|
|
directly into the handler (not via UserManager),
|
|
setting the confirmed flag in-memory here.
|
|
end note
|
|
|
|
ConfirmHandler -> UserMgr: UpdateAsync(user)
|
|
activate UserMgr
|
|
UserMgr -> UserStore: UpdateAsync(user)
|
|
activate UserStore
|
|
UserStore -> DB: UPDATE dbo.UserAccount SET ...
|
|
UserStore -> DB: INSERT INTO dbo.UserVerification\n(UserAccountId, VerificationDateTime)
|
|
note right of UserStore
|
|
Idempotent: skipped (duplicate-key
|
|
exception swallowed) if a verification
|
|
row already exists.
|
|
end note
|
|
UserStore --> UserMgr: IdentityResult.Success
|
|
deactivate UserStore
|
|
UserMgr --> ConfirmHandler: IdentityResult
|
|
deactivate UserMgr
|
|
|
|
ConfirmHandler --> Mediator: ConfirmationPayload(\n user.Id, DateTime.UtcNow)
|
|
deactivate ConfirmHandler
|
|
Mediator --> API: ConfirmationPayload
|
|
deactivate Mediator
|
|
|
|
API -> User: 200 OK\n{message: "User with ID {id} is confirmed.",\npayload: {...}}
|
|
end
|
|
end
|
|
deactivate API
|
|
|
|
@enduml
|