fullstackhero/dotnet-starter-kit · error · UnauthorizedException

user is deactivated

Error message

user is deactivated

What it means

ValidateUserStatus throws UnauthorizedException("user is deactivated") when the FshUser's IsActive flag is false. This check runs on credential login, refresh-token validation, and claim building, so a deactivated user cannot obtain or renew tokens.

Solutions

  1. Re-activate the user (admin endpoint or set IsActive=true on the FshUser row) if deactivation was unintended.
  2. If deactivation is correct, stop the client from retrying and switch to a valid active account.
  3. For service accounts, provision an active dedicated user instead of reusing offboarded accounts.

Example fix

// before
var user = await db.Users.FirstAsync(u => u.Email == email); // IsActive = false
// after (admin reactivation)
user.IsActive = true; await db.SaveChangesAsync(ct);
Defensive patterns

Strategy: try-catch

Validate before calling

// if the API exposes a profile/status endpoint, check before retrying auth
const user = await api.get('/api/users/current');
if (user && user.isActive === false) { showDeactivatedScreen(); return; }

Type guard

function isActiveUser(u: { isActive: boolean } | null | undefined): u is { isActive: true } {
  return u?.isActive === true;
}

Try / catch

catch (ApiError e) when (e.StatusCode === 401 && e.Message.includes('deactivated')) {
  clearTokens();
  showMessage('This account has been deactivated. Contact your administrator.');
}

Prevention

When it happens

Trigger: Login or refresh with credentials of a user whose IsActive column is false — typically after an admin deactivated the user, a DeleteUser soft-delete, or a self-deactivation flow.

Common situations: Offboarding: admin deactivates an account whose session tokens are still in use by a client; automated jobs authenticating with a deactivated service user; a user re-activated but the client still holds tokens issued while deactivated and refreshes fail.

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


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

Appendix: source

Thrown at src/Modules/Identity/Modules.Identity/Services/IdentityService.cs:268

    }

    private void ValidateRefreshTokenExpiry(FshUser user)
    {
        var now = _timeProvider.GetUtcNow().UtcDateTime;
        if (user.RefreshTokenExpiryTime <= now)
        {
            _logger.LogWarning(
                "Refresh token expired for user {UserId}. Expired at: {ExpiryTime}, Current time: {CurrentTime}",
                user.Id, user.RefreshTokenExpiryTime, now);
            throw new UnauthorizedException("refresh token is invalid or expired");
        }
    }

    private static void ValidateUserStatus(FshUser user)
    {
        if (!user.IsActive)
        {
            throw new UnauthorizedException("user is deactivated");
        }

        if (!user.EmailConfirmed)
        {
            throw new UnauthorizedException("email not confirmed");
        }
    }

    private void ValidateTenantStatus(AppTenantInfo tenant)
    {
        if (tenant.Id == MultitenancyConstants.Root.Id)
        {
            return;
        }

        if (!tenant.IsActive)
        {
            throw new UnauthorizedException($"tenant {tenant.Id} is deactivated");

View on GitHub (pinned to 3f2959e683)