fullstackhero/dotnet-starter-kit · error · UnauthorizedException

no current user

Error message

no current user

What it means

UpdateChannelCommandHandler throws UnauthorizedException("no current user") when the injected ICurrentUser.GetUserId() returns Guid.Empty, meaning no authenticated user is associated with the request. This is a guard at the top of Handle so channel updates never run without an identity. It surfaces as an HTTP 401 via the module's exception middleware.

Solutions

  1. Ensure the caller sends a valid Authorization: Bearer <token> header with an unexpired JWT.
  2. Verify the endpoint is protected with [Authorize] and UseAuthentication/UseAuthorization run before the endpoint mapping.
  3. Fix token acquisition on the client (refresh flow) if the token is expired or invalid.
  4. In tests, mock ICurrentUser.GetUserId() to return a real Guid instead of Guid.Empty.

Example fix

// before
var client = factory.CreateClient(); // no auth header
await client.PutAsJsonAsync($"/api/v1/channels/{id}", req);

// after
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
await client.PutAsJsonAsync($"/api/v1/channels/{id}", req);
Defensive patterns

Strategy: try-catch

Validate before calling

const token = await getValidAccessToken();
if (!token) throw new Error("not authenticated — login before updating a channel");

Type guard

function isAuthenticated(user: { id?: string }): user is { id: string } {
  return typeof user.id === "string" && user.id.length > 0;
}

Try / catch

try {
  await api.updateChannel(cmd);
} catch (e) {
  if (e.status === 401) { await refreshToken(); retry(); }
  else throw e;
}

Prevention

When it happens

Trigger: Calling the update-channel endpoint (PUT/POST for a channel) with a missing, expired, or malformed JWT, or invoking the handler directly (e.g. from a background job or test) without authenticating a user context.

Common situations: Anonymous or unauthenticated HTTP requests reaching the endpoint because [Authorize] was omitted or middleware order is wrong; an expired access token the client did not refresh; unit/integration tests constructing the handler with a mocked ICurrentUser returning Guid.Empty.

Related errors


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

Appendix: source

Thrown at src/Modules/Chat/Modules.Chat/Features/v1/Channels/UpdateChannel/UpdateChannelCommandHandler.cs:20

using FSH.Framework.Core.Exceptions;
using FSH.Modules.Chat.Contracts.v1.Commands;
using FSH.Modules.Chat.Data;
using FSH.Modules.Chat.Features.v1.Internal;
using Mediator;
using Microsoft.EntityFrameworkCore;

namespace FSH.Modules.Chat.Features.v1.Channels.UpdateChannel;

public sealed class UpdateChannelCommandHandler(
    ChatDbContext db,
    ICurrentUser currentUser)
    : ICommandHandler<UpdateChannelCommand, Unit>
{
    public async ValueTask<Unit> Handle(UpdateChannelCommand cmd, CancellationToken cancellationToken)
    {
        ArgumentNullException.ThrowIfNull(cmd);
        var userId = currentUser.GetUserId();
        if (userId == Guid.Empty) throw new UnauthorizedException("no current user");

        var channel = await db.Channels.FirstOrDefaultAsync(c => c.Id == cmd.ChannelId, cancellationToken)
            .ConfigureAwait(false)
            ?? throw new NotFoundException("Channel not found.");

        channel.RequireAdmin(userId.ToString());
        channel.Rename(cmd.Name, cmd.Description);
        channel.SetPrivate(cmd.IsPrivate);

        await db.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
        return Unit.Value;
    }
}

View on GitHub (pinned to 3f2959e683)