@startuml resend-confirmation !include ../../../pipeline/diagrams/biergarten-weizen-theme.puml skinparam sequenceMessageAlign center skinparam maxMessageSize 200 title User Authentication Flow - Resend Confirmation Email actor User participant "API\nController" as API participant "MediatR pipeline\n(ValidationBehavior)" as Mediator box "Handler Layer" #F1F3EA participant "ResendConfirmationEmailHandler" as ResendHandler participant "UserManager" as UserMgr participant "TokenService" as TokenSvc end box box "Infrastructure Layer" #EBECE3 participant "Email\nProvider" as SMTP participant "Template\nProvider" as Template end box box "Repository / Cross-slice" #DBEEDD participant "DapperUserStore\n(IUserStore/IUserPasswordStore/\nIUserEmailStore)" as UserStore participant "EmailDispatcher\n(Features.Emails)" as EmailDispatcher end box database "SQL Server" as DB note over UserMgr ASP.NET Core Identity's UserManager 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). end note User -> API: POST /api/auth/confirm/resend?userId=\n(requires Bearer access token) activate API API -> Mediator: Send(ResendConfirmationEmailCommand(userId)) activate Mediator Mediator -> ResendHandler: Handle(command) activate ResendHandler ResendHandler -> UserMgr: FindByIdAsync(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 --> ResendHandler: ApplicationUser or null deactivate UserMgr alt User not found ResendHandler -> ResendHandler: return silently\n(prevents user enumeration) else User found ResendHandler -> UserMgr: IsEmailConfirmedAsync(user) UserMgr --> ResendHandler: bool alt Already confirmed ResendHandler -> ResendHandler: return silently (no-op) else Not yet confirmed ResendHandler -> TokenSvc: GenerateConfirmationToken(user) activate TokenSvc TokenSvc --> ResendHandler: confirmation token deactivate TokenSvc ResendHandler -> Mediator: Send(SendResendConfirmationEmailCommand) activate Mediator Mediator -> EmailDispatcher: SendResendConfirmationEmailAsync(\n firstName, email, confirmationToken) activate EmailDispatcher EmailDispatcher -> Template: Render email\n(same Razor pipeline as registration) EmailDispatcher -> SMTP: SendAsync(email, subject, body) deactivate EmailDispatcher deactivate Mediator end end ResendHandler --> Mediator: (void) deactivate ResendHandler Mediator --> API: (void) deactivate Mediator API -> User: 200 OK\n{message: "confirmation email has been resent"} note right of API Always 200 OK regardless of outcome above - same user-enumeration precaution as a not-found user or an already-confirmed account. end note deactivate API @enduml