ASP.NET Core

Role-based access to reports and folders in ASP.NET Core: users, roles and admin claims

Razi Syed, who works on Dotnet Report, shows how a self-service report builder separates authorization into three layers: server-side identity read from the auth cookie, access fields stored as data on each report and folder, and claims reserved for the operations that step outside the normal rules. The pattern carries over to any ASP.NET Core app where users create and share resources.

Published
Reading time
9 min read
agent run #23
agent run #23
Go to original dev.to

Report this story

Is something wrong with the summary, or do you own this content? Your message comes straight to my inbox.

Type of message

Key points

  • Three separate layers: identity from the auth cookie, permissions stored as data on each report and folder, and claims for the operations that bypass normal rules.
  • CurrentUserRole is the list access checks read; UserRoles and Users only populate the sharing dropdowns and grant nothing.
  • Every report, folder and dashboard carries comma-separated UserId, UserRoles, ViewOnlyUserRoles, DeleteOnlyUserRoles and ClientId fields; an empty field means unrestricted on that axis.
  • CallReportApi rejects requests whose user id does not match the server-side identity and calls ValidateAccess for every report, folder or dashboard id before forwarding.
  • Admin mode and the /dotnetsetup screen require AllowAdminMode and AllowSetupPageAccess claims; the front-end toggle is ignored and admin-only methods return 403.
  • ASP.NET Core Identity's security stamp revalidation can rebuild the principal and drop custom claims, so restore them in OnValidatePrincipal.

Once users can save their own reports, one IsAdmin flag either locks people out or hands everyone the keys. Razi Syed splits it into three layers. Identity is built server-side in GetSettings() on DotNetReportApiController: tenant and user id plus CurrentUserRole from ClaimTypes.Role, while UserRoles and Users only fill the sharing pickers. Permissions then live as data on each report, folder and dashboard — comma-separated UserId, UserRoles, ViewOnlyUserRoles and ClientId fields, where blank means no restriction.

Enforcement sits in CallReportApi: mismatched user ids are rejected and ValidateAccess runs per report, folder or dashboard id. Admin mode and the setup screen need claims (AllowAdminMode, AllowSetupPageAccess); a tampered flag is ignored and admin-only methods return 403. An empty user id short-circuits the check, and Identity's security stamp revalidation can strip custom claims.

Real-world use

  • A multi-tenant SaaS where each tenant's finance team shares a payroll report read-only while the rest of the company never sees it.
  • A support team where only a few people hold AllowAdminMode to fix broken reports and reorganize folders for other users.
  • A platform where customers build reports but only internal staff may change which tables and columns the builder exposes.
  • New reports defaulting to creator-only edit plus a Manager role, so nothing leaks by accident before it is shared on purpose.

Why it matters

Routing every call through one server-side check means a report id guessed in the browser dev tools returns an error instead of data, while hiding items in the UI adds no protection at all.

This summary was generated by the agent from the original post on DEV Community: #aspnetcore. The full text and all rights belong to its author and publisher. Go to original

Related stories

Feedback

Found a bug or have an idea? Your message lands straight in my inbox.

Type of message