fullstackhero/dotnet-starter-kit · error · UnauthorizedException

authenticated user required to toggle status

Error message

authenticated user required to toggle status

What it means

Thrown by BuildToggleContextAsync when currentUser.GetUserId() returns Guid.Empty, meaning there is no authenticated user on the current request (or the claims principal lacks the id claim). Activating/deactivating users requires knowing the acting user.

Solutions

  1. Re-authenticate so a valid JWT with the user id claim is attached to the request.
  2. Check the token's claims include the identifier claim mapped by ICurrentUser.
  3. Confirm the endpoint is behind [Authorize]/authentication middleware.
  4. For internal calls, run under an authenticated principal rather than none.

Example fix

// before: request sent without auth header
await apiFetch(`/api/v1/users/${id}/toggle-status`, { method: "POST" });
// after
await apiFetch(`/api/v1/users/${id}/toggle-status`, { method: "POST", headers: authHeader() });
Defensive patterns

Strategy: type-guard

Validate before calling

function hasAuthContext(token) {
  const claims = decodeJwt(token);
  return Boolean(claims && (claims.sub || claims.uid));
}

Type guard

function isAuthed(user) {
  return typeof user?.id === "string" && /^[0-9a-fA-F-]{36}$/.test(user.id) && user.id !== "00000000-0000-0000-0000-000000000000";
}

Try / catch

try { await toggleStatus(userId); } catch (e) {
  if (e.status === 401 && /authenticated user required/i.test(e.message)) { await relogin(); return retry(); }
  throw e;
}

Prevention

When it happens

Trigger: User activate/deactivate endpoints called without a valid JWT, or with a token missing the standard name/sub identifier claim the ICurrentUser service maps.

Common situations: Expired or anonymous tokens reaching the endpoint because [Authorize] was bypassed; custom token issuance dropping the uid claim; service-layer calls without an HttpContext user.

Understand the failure class

Related errors


AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15). Data as JSON: /api/errors/10fe7e8ce8d29ff7. Report an issue: GitHub.

Appendix: source

Thrown at src/Modules/Identity/Modules.Identity/Services/UserStatusService.cs:55

    }

    private void EnsureValidTenant()
    {
        if (string.IsNullOrWhiteSpace(multiTenantContextAccessor?.MultiTenantContext?.TenantInfo?.Id))
        {
            throw new UnauthorizedException("invalid tenant");
        }
    }

    private async Task<ToggleStatusContext> BuildToggleContextAsync(
        string userId,
        bool activateUser,
        CancellationToken cancellationToken)
    {
        var actorId = currentUser.GetUserId();
        if (actorId == Guid.Empty)
        {
            throw new UnauthorizedException("authenticated user required to toggle status");
        }

        var actor = await userManager.FindByIdAsync(actorId.ToString())
            ?? throw new UnauthorizedException("current user not found");

        var targetUser = await userManager.Users
            .Where(u => u.Id == userId)
            .FirstOrDefaultAsync(cancellationToken)
            ?? throw new NotFoundException("User Not Found.");

        return new ToggleStatusContext(
            ActorId: actorId,
            Actor: actor,
            TargetUser: targetUser,
            ActivateUser: activateUser,
            TenantId: multiTenantContextAccessor?.MultiTenantContext?.TenantInfo?.Id);
    }

View on GitHub (pinned to 3f2959e683)