mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-10-14 17:54:42 +00:00
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> for assistance with diagram creation and documentation prose updates.
78 lines
3.2 KiB
Plaintext
78 lines
3.2 KiB
Plaintext
sequenceDiagram
|
|
actor User
|
|
participant API as API Controller
|
|
participant Mediator as MediatR pipeline<br/>(ValidationBehavior)
|
|
box Handler Layer
|
|
participant RefreshHandler as RefreshTokenHandler
|
|
participant UserMgr as UserManager(ApplicationUser)
|
|
participant TokenSvc as TokenService
|
|
end
|
|
box Infrastructure Layer
|
|
participant JWT as JWT Infrastructure
|
|
end
|
|
box Repository / Cross-slice
|
|
participant UserStore as DapperUserStore<br/>(IUserStore/IUserPasswordStore/IUserEmailStore)
|
|
end
|
|
participant DB as SQL Server
|
|
|
|
User->>API: POST /api/auth/refresh<br/>Header: X-Refresh-Token: {token}
|
|
activate API
|
|
API->>Mediator: Send(RefreshTokenCommand(refreshToken))
|
|
activate Mediator
|
|
Note right of Mediator: ValidationBehavior runs RefreshTokenValidator — refreshToken must be non-empty. Anonymous endpoint, no Bearer token required.
|
|
|
|
alt Validation fails
|
|
Mediator->>API: throws FluentValidation.ValidationException
|
|
API->>User: 400 Bad Request {message, errors}
|
|
else Validation succeeds
|
|
Mediator->>RefreshHandler: Handle(command)
|
|
activate RefreshHandler
|
|
|
|
RefreshHandler->>TokenSvc: RefreshTokenAsync(refreshToken)
|
|
activate TokenSvc
|
|
TokenSvc->>JWT: ValidateRefreshTokenAsync(refreshToken)
|
|
activate JWT
|
|
Note right of JWT: Validates signature/expiry against the refresh token secret, extracts userId/username claims
|
|
JWT-->>TokenSvc: ValidatedToken or throws
|
|
deactivate JWT
|
|
|
|
alt Token invalid or expired
|
|
TokenSvc->>RefreshHandler: throw UnauthorizedException
|
|
RefreshHandler->>Mediator: propagate
|
|
Mediator->>API: propagate
|
|
API->>User: 401 Unauthorized
|
|
else Token valid
|
|
TokenSvc->>UserMgr: FindByIdAsync(validated.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-->>TokenSvc: ApplicationUser or null
|
|
deactivate UserMgr
|
|
|
|
alt User not found
|
|
TokenSvc->>RefreshHandler: throw UnauthorizedException "User account not found"
|
|
RefreshHandler->>Mediator: propagate
|
|
Mediator->>API: propagate
|
|
API->>User: 401 Unauthorized
|
|
else User found
|
|
TokenSvc->>JWT: GenerateAccessToken(user) + GenerateRefreshToken(user)
|
|
JWT-->>TokenSvc: new access + refresh tokens
|
|
TokenSvc-->>RefreshHandler: RefreshTokenResult
|
|
deactivate TokenSvc
|
|
|
|
RefreshHandler-->>Mediator: LoginPayload(userId, username, refreshToken, accessToken)
|
|
deactivate RefreshHandler
|
|
Mediator-->>API: LoginPayload
|
|
deactivate Mediator
|
|
API->>User: 200 OK {message: "Token refreshed successfully.", payload: {...}}
|
|
end
|
|
end
|
|
end
|
|
deactivate API
|