fullstackhero/dotnet-starter-kit · warning · UnauthorizedException
Unauthorized
Error message
Unauthorized
What it means
DisableTwoFactorCommandHandler throws UnauthorizedException when ICurrentUser.IsAuthenticated() returns false, meaning the request reached the handler without a valid authenticated user. It is a last-line guard protecting the 2FA disable flow, which must never run for anonymous callers.
Solutions
- Log in again to obtain a fresh JWT and retry with the Authorization: Bearer header
- Verify the endpoint is not marked [AllowAnonymous] and that UseAuthentication/UseAuthorization run before endpoint mapping in the correct order
- Check JWT validation settings (issuer, audience, signing key, ClockSkew) so valid tokens are not rejected
- In tests, stub ICurrentUser to return IsAuthenticated = true before calling the handler
Example fix
// before
client.DefaultRequestHeaders.Remove("Authorization");
// after
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", freshToken); Defensive patterns
Strategy: try-catch
Validate before calling
function canDisable2fa() {
return Boolean(accessToken) && !isTokenExpired(accessToken);
}
if (!canDisable2fa()) await reauthenticate(); Try / catch
try {
await api.disableTwoFactor(cmd);
} catch (e) {
if (e.status === 401) { await reauthenticate(); return retry(); }
throw e;
} Prevention
- Refresh the access token before long-running account-security flows
- Attach the Authorization header centrally in the API client, not per call
- Never call protected endpoints from unauthenticated contexts
When it happens
Trigger: Calling the disable-2FA endpoint with no Authorization header, an expired/invalid JWT, a token missing the required claims after the endpoint's [AllowAnonymous] misconfiguration, or invoking the handler directly in tests without a mocked authenticated ICurrentUser.
Common situations: Expired access token after idle time; clock skew invalidating the JWT; auth middleware misconfigured or omitted on the route; a test harness that forgot to authenticate the current-user service.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15).
Data as JSON: /api/errors/91372365dcbb97ce.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Identity/Modules.Identity/Features/v1/TwoFactor/Disable/DisableTwoFactorCommandHandler.cs:29
: ICommandHandler<DisableTwoFactorCommand, bool>
{
private readonly UserManager<FshUser> _userManager;
private readonly ICurrentUser _currentUser;
public DisableTwoFactorCommandHandler(UserManager<FshUser> userManager, ICurrentUser currentUser)
{
_userManager = userManager;
_currentUser = currentUser;
}
public async ValueTask<bool> Handle(
DisableTwoFactorCommand command, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(command);
if (!_currentUser.IsAuthenticated())
{
throw new UnauthorizedException();
}
var userId = _currentUser.GetUserId().ToString();
var user = await _userManager.FindByIdAsync(userId)
?? throw new NotFoundException($"User {userId} not found.");
// Require current password so a stolen access token alone can't downgrade
// account security.
if (!await _userManager.CheckPasswordAsync(user, command.CurrentPassword))
{
throw new UnauthorizedException("Current password is incorrect.");
}
await _userManager.SetTwoFactorEnabledAsync(user, false);
await _userManager.ResetAuthenticatorKeyAsync(user);
return true;
}
}View on GitHub (pinned to 3f2959e683)