fullstackhero/dotnet-starter-kit · error · ForbiddenException
Cross-tenant audit summary requires…
Error message
Cross-tenant audit summary requires Permissions.AuditTrails.ViewCrossTenant.
What it means
GetAuditSummaryQueryHandler.BuildBaseQueryAsync throws ForbiddenException when the caller lacks the AuditingPermissions.AuditTrails.ViewCrossTenant permission but requested a cross-tenant summary. The query needs IgnoreQueryFilters() to span tenants, which is only permitted for holders of that permission. Maps to HTTP 403.
Solutions
- Grant the AuditingPermissions.AuditTrails.ViewCrossTenant permission to the caller's role.
- If cross-tenant scope was not intended, request the summary scoped to the caller's own tenant.
- Re-run permission/claim seeding (DbMigrator --seed) so roles include the new permission.
- Use a root-operator account for platform-wide audit summaries.
Example fix
// before role.Permissions = [Permissions.Users.View]; // after role.Permissions = [Permissions.Users.View, AuditingPermissions.AuditTrails.ViewCrossTenant];
Defensive patterns
Strategy: validation
Validate before calling
bool canView = await permissions.HasPermissionAsync(userId, AuditingPermissions.AuditTrails.ViewCrossTenant, ct); if (!canView) scopeSummaryToOwnTenant();
Try / catch
try { return await api.GetAuditSummary(request); } catch (HttpRequestException e) when (e.StatusCode == HttpStatusCode.Forbidden) { return Results.Forbid(); } Prevention
- Gate cross-tenant filters behind a permission check in the UI
- Re-run permission seeding after adding new auditing permissions
- Default summary queries to the caller's own tenant
When it happens
Trigger: Calling the audit-summary endpoint with a cross-tenant request (tenant filter matching multiple tenants or 'all') while the authenticated user's permission set does not include ViewCrossTenant.
Common situations: Tenant admin attempting platform-wide reporting; role definitions missing the ViewCrossTenant claim after seeding; permission renamed/moved in a newer version so older roles lack it.
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
- Cross-tenant audit access requires…
- Tenant context is required.
- Only the root operator may generate invoices across tenants.
- Tenant context is required.
- Tenant context is required.
AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15).
Data as JSON: /api/errors/d0f5314a8f9b21bc.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Auditing/Modules.Auditing/Features/v1/GetAuditSummary/GetAuditSummaryQueryHandler.cs:107
var currentTenant = _currentUser.GetTenant();
var requested = string.IsNullOrWhiteSpace(query.TenantId) ? null : query.TenantId;
bool wantsCrossTenant =
requested is not null
&& !string.Equals(requested, currentTenant, StringComparison.OrdinalIgnoreCase);
if (!wantsCrossTenant)
{
return _dbContext.AuditRecords.AsNoTracking();
}
var userId = _currentUser.GetUserId().ToString();
var allowed = await _permissions
.HasPermissionAsync(userId, AuditingPermissions.AuditTrails.ViewCrossTenant, ct)
.ConfigureAwait(false);
if (!allowed)
{
throw new ForbiddenException("Cross-tenant audit summary requires Permissions.AuditTrails.ViewCrossTenant.");
}
return _dbContext.AuditRecords
.AsNoTracking()
.IgnoreQueryFilters()
.Where(a => a.TenantId == requested);
}
private (DateTime FromUtc, DateTime ToUtc) ResolveWindow(DateTime? from, DateTime? to)
{
var now = _timeProvider.GetUtcNow().UtcDateTime;
var resolvedTo = to ?? now;
var resolvedFrom = from ?? resolvedTo - DefaultWindow;
if (resolvedTo - resolvedFrom > MaxWindow)
{
resolvedFrom = resolvedTo - MaxWindow;
}View on GitHub (pinned to 3f2959e683)