fullstackhero/dotnet-starter-kit · error · UnauthorizedException
no current user
Error message
no current user
What it means
UnauthorizedException('no current user') is thrown when currentUser.GetUserId() returns Guid.Empty, meaning the request reached the handler without a resolvable authenticated user id, even though a tenant was present. The handler requires both a tenant and a concrete user to attribute and authorize the upload.
Solutions
- Attach a valid, non-expired Bearer access token to the request and retry.
- Decode the JWT and confirm the user identifier claim (sub/nameidentifier) exists and maps correctly to currentUser.GetUserId().
- Fix token issuance if the identifier claim is missing; check claim-type mapping (JwtSecurityTokenHandler DefaultInboundClaimTypeMap / MapInboundClaims) in ConfigureJwtBearerOptions.
- Ensure the endpoint is not AllowAnonymous and authentication middleware runs before the handler.
Example fix
// before
await apiFetch("/files/v1/request-upload-url", { method: "POST" }); // no Authorization header
// after
await apiFetch("/files/v1/request-upload-url", { method: "POST", headers: { Authorization: `Bearer ${token}` } }); Defensive patterns
Strategy: validation
Validate before calling
if (!token || isExpired(token)) await refreshSession();
const userId = parseJwt(token).sub;
if (!userId) throw new Error("Token has no user identifier claim"); Type guard
function hasUserSubject(claims) { return typeof claims.sub === "string" && claims.sub.length > 0; } Try / catch
try { await requestUploadUrl(cmd); } catch (e) { if (e.status === 401 && e.message === "no current user") { await relogin(); retry(); } else throw e; } Prevention
- Attach the Authorization header centrally in the API client (apiFetch) so it can never be omitted.
- Handle 401 globally by refreshing/expiring the session rather than letting requests proceed.
- After changing claim mappings, force users to re-login to pick up corrected tokens.
When it happens
Trigger: RequestUploadUrlCommand invoked with an anonymous or anonymous-equivalent request: missing/expired/malformed JWT that authentication silently degraded, an endpoint accidentally configured with AllowAnonymous, or a token without the name/sub identifier claim the current-user service maps to GetUserId().
Common situations: Calling the endpoint without the Authorization header; expired token where the 401 challenge was bypassed (e.g. via SignalR query-string token path misconfigured); identity claims renamed or stripped by a custom token factory; integration tests constructing ICurrentUser stubs returning Guid.Empty.
Related errors
AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15).
Data as JSON: /api/errors/8b7b8e8409d4d75d.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Files/Modules.Files/Features/v1/RequestUploadUrl/RequestUploadUrlCommandHandler.cs:35
public sealed class RequestUploadUrlCommandHandler(
FilesDbContext db,
IStorageService storage,
FileAccessPolicyRegistry policies,
IQuotaService quotas,
ICurrentUser currentUser,
IOptions<FilesOptions> options)
: ICommandHandler<RequestUploadUrlCommand, PresignedUploadResponse>
{
public async ValueTask<PresignedUploadResponse> Handle(RequestUploadUrlCommand cmd, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(cmd);
var tenantId = currentUser.GetTenant() ?? throw new UnauthorizedException("invalid tenant");
var userId = currentUser.GetUserId();
if (userId == Guid.Empty)
{
throw new UnauthorizedException("no current user");
}
// Category lookup + extension/size validation.
if (!options.Value.Categories.TryGetValue(cmd.Category, out var category))
{
throw new CustomException($"Unknown category '{cmd.Category}'.", (IEnumerable<string>?)null, HttpStatusCode.BadRequest);
}
var extension = Path.GetExtension(cmd.FileName);
if (string.IsNullOrWhiteSpace(extension) ||
!category.AllowedExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
{
throw new CustomException(
$"Extension '{extension}' not allowed for category '{cmd.Category}'.",
(IEnumerable<string>?)null,
HttpStatusCode.BadRequest);
}
View on GitHub (pinned to 3f2959e683)