Files
the-biergarten-app/docs/website/diagrams-md/auth/resend-confirmation.md

3.1 KiB

User Authentication Flow — Resend Confirmation Email

sequenceDiagram
    actor User
    participant API as API Controller
    participant Mediator as MediatR pipeline<br/>(ValidationBehavior)
    box rgb(241,243,234) Handler Layer
        participant ResendHandler as ResendConfirmationEmailHandler
        participant UserMgr as UserManager(ApplicationUser)
        participant TokenSvc as TokenService
    end
    box rgb(235,236,227) Infrastructure Layer
        participant SMTP as Email Provider
        participant Template as Template Provider
    end
    box rgb(219,238,221) Repository / Cross-slice
        participant UserStore as DapperUserStore<br/>(IUserStore/IUserPasswordStore/IUserEmailStore)
        participant EmailDispatcher as EmailDispatcher<br/>(Features.Emails)
    end
    participant DB as SQL Server

    Note over UserMgr: ASP.NET Core Identity's UserManager replaces the deleted AuthRepository — handlers delegate to UserManager, which calls DapperUserStore (persistence) and Argon2PasswordHasher.

    User->>API: POST /api/auth/confirm/resend?userId={id} (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 ... FROM UserAccount WHERE 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 (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(firstName, email, confirmationToken)
            activate EmailDispatcher
            EmailDispatcher->>Template: Render email (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 {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
    deactivate API