fullstackhero/dotnet-starter-kit · error · ForbiddenException
Only administrators can change user status.
Error message
Only administrators can change user status.
What it means
UserStatusService.ValidateTogglePermissionsAsync throws ForbiddenException("Only administrators can change user status.") when the authenticated actor is not a member of the Admin role. The failure is first recorded via AuditPolicyFailureAsync before the 403 is raised.
Solutions
- Grant the actor the Admin role (assign via the identity module roles endpoint or seed) if they legitimately need this capability.
- Sign in as a user with the Admin role instead.
- Verify role assignment: check AspNetUserRoles/AspNetRoles for the actor and RoleConstants.Admin.
- Re-login to refresh the JWT claims if the role was recently granted.
Example fix
// before (acting as non-admin)
await mediator.Send(new ToggleUserStatusCommand { UserId = targetId });
// after (assert first)
if (!await userManager.IsInRoleAsync(currentUser, RoleConstants.Admin))
throw new ForbiddenException("Admin role required.");
await mediator.Send(new ToggleUserStatusCommand { UserId = targetId }); Defensive patterns
Strategy: validation
Validate before calling
var isAdmin = await userManager.IsInRoleAsync(actor, RoleConstants.Admin);
if (!isAdmin) return Result.Forbidden("Admin role required."); Try / catch
try { ... }
catch (ForbiddenException) { notifyUser("You need administrator rights for this action"); } Prevention
- Gate the UI so the toggle action is only shown to admins (RouteGuard/permission check).
- Mirror the permission in the frontend permissions catalog.
- Test role changes with fresh tokens after granting/revoking roles.
- Audit role assignments regularly in production tenants.
When it happens
Trigger: Any non-admin user calling the toggle-user-status endpoint; an admin whose role assignment was removed before the call; a token issued before a role revocation still carrying the user through.
Common situations: Ordinary users hitting admin-only endpoints from a modified UI; permission config drift where the Admin role constant changed; users with custom roles assumed to have admin rights.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- Channel admin role required.
- not allowed to change this file's visibility
- not allowed to delete this file
- not your pending file
- System groups cannot be deleted.
AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15).
Data as JSON: /api/errors/32fd625bcc35eeae.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Identity/Modules.Identity/Services/UserStatusService.cs:81
.FirstOrDefaultAsync(cancellationToken)
?? throw new NotFoundException("User Not Found.");
return new ToggleStatusContext(
ActorId: actorId,
Actor: actor,
TargetUser: targetUser,
ActivateUser: activateUser,
TenantId: multiTenantContextAccessor?.MultiTenantContext?.TenantInfo?.Id);
}
private async Task ValidateTogglePermissionsAsync(
ToggleStatusContext context,
CancellationToken cancellationToken)
{
if (!await userManager.IsInRoleAsync(context.Actor, RoleConstants.Admin))
{
await AuditPolicyFailureAsync(context, "ActorNotAdmin", cancellationToken);
throw new ForbiddenException("Only administrators can change user status.");
}
if (!context.ActivateUser && context.ActorId.ToString() == context.TargetUser.Id)
{
await AuditPolicyFailureAsync(context, "SelfDeactivationBlocked", cancellationToken);
throw new CustomException("Users cannot deactivate themselves.", Array.Empty<string>(), HttpStatusCode.BadRequest);
}
if (!context.ActivateUser && await userManager.IsInRoleAsync(context.TargetUser, RoleConstants.Admin))
{
await AuditPolicyFailureAsync(context, "AdminDeactivationBlocked", cancellationToken);
throw new CustomException("Administrators cannot be deactivated.", Array.Empty<string>(), HttpStatusCode.BadRequest);
}
if (!context.ActivateUser)
{
await EnsureMinimumActiveAdminsAsync(context, cancellationToken);
}View on GitHub (pinned to 3f2959e683)