Role-Based Authorization in .NET using JWT and LiteDB


In this post, we’ll walk through building a clean and modular Role-Based Authorization system in a .NET 8 Web API. Using JWT for authentication and LiteDB for storage, we’ll demonstrate how to restrict access to endpoints based on user roles like Admin
, Manager
, and Employee
.
Project Overview
Here’s a high-level summary of what we’ve implemented:
JWT Authentication
Role-based Authorization at controller and action level
Clean Architecture: DTOs, Services, Interfaces, and Repositories
NoSQL Storage using LiteDB
Seeded roles + users + products on app startup
Folder Structure
Roles We Support
We’ve defined three roles in our app:
public static class RoleConstants
{
public const string Admin = "Admin";
public const string Manager = "Manager";
public const string Employee = "Employee";
}
And grouped them like this:
public static class RolePolicies
{
public const string Admin = $"{RoleConstants.Admin}";
public const string AdminOrManager = $"{RoleConstants.Admin},{RoleConstants.Manager}";
public const string All = $"{RoleConstants.Admin},{RoleConstants.Manager},{RoleConstants.Employee}";
}
JWT Authentication
We’re using JWT bearer tokens for authentication. We have a login endpoint POST /api/auth/login
that can be used to login with the seeded admin, manager and employee users:
var admin = new Role { Id = 1, Name = RoleConstants.Admin };
var manager = new Role { Id = 2, Name = RoleConstants.Manager };
var employee = new Role { Id = 3, Name = RoleConstants.Employee };
var adminUser = new User { Id = 1, Username = "admin", Password = "admin123", RoleId = 1 };
var managerUser = new User { Id = 2, Username = "manager", Password = "manager123", RoleId = 2 };
var employeeUser = new User { Id = 3, Username = "employee", Password = "emp123", RoleId = 3 };
If credentials match, we issue a token containing the user's role.
The token must be sent with every protected request via the Authorization header.
(If you're not sure how to enable JWT authentication in Swagger UI, we explained it step-by-step in our earlier blog post:
How to Enable JWT Authentication in Swagger (.NET 8))
Securing the Product Controller
This is the most important piece — let’s restrict access using [Authorize]
based on user roles.
[Route("api/[controller]")]
[ApiController]
public class ProductController : ControllerBase
{
private readonly IProductService _service;
public ProductController(IProductService service)
{
_service = service;
}
[HttpGet]
[Authorize]
public async Task<IActionResult> GetAll()
{
var response = _service.GetAll();
return Ok(response);
}
[HttpGet("{id}")]
[Authorize]
public async Task<IActionResult> GetById(int id)
{
var response = _service.GetById(id);
return Ok(response);
}
[Authorize(Roles = RolePolicies.AdminOrManager)]
[HttpPost]
public async Task<IActionResult> Add([FromBody] AddProductDto dto)
{
var response = _service.Add(dto);
return Ok(response);
}
[Authorize(Roles = RolePolicies.AdminOrManager)]
[HttpPut]
public async Task<IActionResult> Update([FromBody] UpdateProductDto dto)
{
var response = _service.Update(dto);
return Ok(response);
}
[Authorize(Roles = RolePolicies.Admin)]
[HttpDelete("{id}")]
public async Task<IActionResult> Delete(int id)
{
var response = _service.Delete(id);
return Ok(response);
}
}
The real power of [Authorize(Roles = "...")]
is fine-grained access control per endpoint.
As per the permissions on the endpoints, every role has the access to GetAll
and GetById
endpoint. If we login with any user we will be able to see the results:
User with Admin
or Manager
role can add product:
But user with Employee
role cannot add product as this role does not have the rights to access Add
endpoint:
Same permissions are there on the Update
endpoint as that of Add
so only Admin
and Manager
role users can update product and Employee
role users will get 403 error (as in above image) if they try to access Update
endpoint.
As per the code, only role which can delete a product is Admin
:
Manager
and Employee
role users will not have access to this endpoint:
Summary
With just a few [Authorize]
attributes and some initial seeding logic, we now have a complete Role-Based Authorization system:
Admin
can perform all actionsManager
can add/update productsEmployee
can only view products
GitHub Source Code
Check out the complete project on GitHub.
Subscribe to my newsletter
Read articles from Waleed Naveed directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
