Role-Based Basic Authentication in ASP.NET Core Web API

Role-Based Basic Authentication in ASP.NET Core Web API

In this article, I will discuss how to implement Role-Based Basic Authentication in ASP.NET Core Web API Application with an example. Please read our previous article discussing implementing Basic Authentication in ASP.NET Core Web API. In this article, I will explain Role-Based Baic Authentication with one Real-time example using ASP.NET Core Web API.

Role-Based Basic Authentication in ASP.NET Core Web API

Role-Based Authentication in ASP.NET Core Web API is actually a combination of two concepts: Authentication and Authorization. Authentication verifies who the user is, and authorization determines what resources a user is allowed to access. In the context of Role-Based Access Control (RBAC), the authorization process uses roles assigned to users to determine whether they have permission to access certain resources or execute specific operations.

What is Role-Based Basic Authentication in ASP.NET Core Web API?

Role-Based Basic Authentication in ASP.NET Core Web API is a mechanism that combines Basic Authentication with Role-Based Authorization. This approach is used to control access to various parts of an API based on the roles assigned to authenticated users.

  • Basic Authentication: Basic Authentication involves sending a username and password with each request, typically encoded in Base64. The server decodes this information and validates the credentials against a stored set of usernames and passwords. If the authentication is successful, the server processes the request; if not, it denies access.
  • Role-Based Authorization: In Role-Based Authorization, the authorization process uses roles assigned to users to determine whether they have permission to access certain resources or execute specific operations. Roles represent a collection of permissions or a level of access control assigned to a user. For example, an “Admin” might have permission to access all API endpoints, whereas a “User” might have limited access.

How to Implement Role-Based Basic Authorization in ASP.NET Core Web API?

Implementing Role-Based Basic Authentication in ASP.NET Core Web API Applications involves creating a custom authentication scheme where user credentials are validated against a store (like a database), and roles are assigned to users to enforce authorization policies on your controllers and actions. The following are the three steps to implement Role-Based Basic Authentication in ASP.NET Core Web API Applications:

  • Step 1: Authentication: The user provides a username and password (base64 encoded string with Basic Authentication) with each API request. The API checks these credentials and, if they are valid, considers the user authenticated.
  • Step 2: Assigning Role Claims to User’s Identity: During the authentication process, claims are generated and associated with the user’s identity. These claims include roles that are associated with the user’s credentials (like “Admin”, “User”, “Manager”, etc.).
  • Step 3. Authorization: Once authenticated, when the user attempts to access a protected resource, the API checks whether the user is authenticated and if their roles allow them to access that protected endpoint. This is typically done using attributes like [Authorize(Roles = “Admin”)], which specifies that only users with the “Admin” role can access the annotated endpoint.

Implementing Role-Based Basic Authentication in ASP.NET Core Web API

Let us implement Role-Based Basic Authentication in the ASP.NET Core Web API Application step by step. Also, we will create a client application consuming the API Endpoints to understand how Role Based Authentication works.

Creating Server Application

Create a new ASP.NET Core Web API Project with the name RoleBasedBasicAuthenticationDemo. This will be our server application, i.e., we will develop restful services that will be consumed by the client application using role-based basic authentication. Once you create the project, please install the following Entity Framework Core packages by executing the following commands in the Package Manager Console:

  • Install-Package Microsoft.EntityFrameworkCore.SqlServer
  • Install-Package Microsoft.EntityFrameworkCore.Tools

Creating Entity Models

Models represent the data structures of your application. They define the shape of the data and the relationships between different entities. Entity models represent the database schema and encapsulate the application’s data. They are mapped to database tables. First, create a folder named Models in the project root directory, where we will create all our models. We will define four primary entities as follows:

  • User: Represents application users.
  • Role: Represents different roles (e.g., Admin, User).
  • UserRole: Joining entity for the many-to-many relationship between Users and Roles.
  • Product: Represents products in the system.
User Entity

Create a class file named User.cs within the Models folder and copy and paste the following code. Represents the user data in the system. Stores essential user details like first name, last name, email, and password. Defines navigation properties to establish relationships with roles via the UserRole entity.

using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;

namespace RoleBasedBasicAuthenticationDemo.Models
{
    [Index(nameof(Email), Name = "Index_Email_Unique", IsUnique = true)]
    public class User
    {
        [Key]
        public int Id { get; set; }

        [Required]
        [StringLength(50)]
        public string FirstName { get; set; }

        [Required]
        [StringLength(50)]
        public string LastName { get; set; }

        [Required]
        [StringLength(100)]
        public string Email { get; set; }

        [Required]
        public string Password { get; set; }

        // Navigation property for the many-to-many relationship with Role
        public ICollection<UserRole> UserRoles { get; set; } = new List<UserRole>();
    }
}
Role Entity

Create a class file named Role.cs within the Models folder and copy and paste the following code. Represents a user role (e.g., Admin, User). Provides the Name field for defining different roles. Includes navigation properties to associate multiple users via the UserRole entity.

using System.ComponentModel.DataAnnotations;

namespace RoleBasedBasicAuthenticationDemo.Models
{
    public class Role
    {
        [Key]
        public int Id { get; set; }

        [Required]
        [StringLength(50)]
        public string Name { get; set; }
        public string? Description { get; set; }

        // Navigation property for the many-to-many relationship with User
        public ICollection<UserRole> UserRoles { get; set; } = new List<UserRole>();
    }
}
UserRole Entity

Create a class file named UserRole.cs within the Models folder and copy and paste the following code. It acts as a join table for the many-to-many relationship between the user and the role. It contains foreign keys (UserId and RoleId) and navigation properties to link users and roles.

namespace RoleBasedBasicAuthenticationDemo.Models
{
    public class UserRole
    {
        public int UserId { get; set; }
        public User User { get; set; }
        public int RoleId { get; set; }
        public Role Role { get; set; }
    }
}
Product Entity

Create a class file named Product.cs within the Models folder and copy and paste the following code. Represents products in the system. Contains details like Name, Description, and Price. Serves as a resource managed and accessed through role-based authentication policies.

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace RoleBasedBasicAuthenticationDemo.Models
{
    public class Product
    {
        [Key]
        public int Id { get; set; }

        [Required]
        [StringLength(100)]
        public string Name { get; set; }

        [StringLength(500)]
        public string? Description { get; set; }

        [Range(0.0, double.MaxValue)]
        [Column(TypeName = "decimal(18, 2)")]
        public decimal Price { get; set; }

        [Required]
        [Range(0, 1000)]
        public int Quantity { get; set; }
    }
}

Data Transfer Objects (DTOs)

DTOs are used to decouple the internal data structure from external communication. That means DTOs are used to transfer data between the client and server in a controlled way, preventing over-posting or exposing sensitive fields. First, create a folder named DTOs in the project root directory where we will create all our Data Transfer Objects. We will define the following DTOs.

  • UserReadDTO
  • UserCreateDTO
  • UserUpdateDTO
  • ProductCreateDTO
  • ProductReadDTO
  • ProductUpdateDTO
UserReadDTO

Create a class file named UserReadDTO.cs within the DTOs folder and copy and paste the following code. Transfers user information from the server to the client without exposing sensitive data like passwords.

namespace RoleBasedBasicAuthenticationDemo.DTOs
{
    public class UserReadDTO
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
        public List<string> Roles { get; set; } = new List<string>();
    }
}
UserCreateDTO

Create a class file named UserCreateDTO.cs within the DTOs folder and copy and paste the following code. Transfers user creation data from the client to the server, excluding the auto-generated ID.

using System.ComponentModel.DataAnnotations;

namespace RoleBasedBasicAuthenticationDemo.DTOs
{
    public class UserCreateDTO
    {
        [Required]
        [StringLength(50, ErrorMessage = "First name cannot exceed 50 characters.")]
        public string FirstName { get; set; }

        [Required]
        [StringLength(50, ErrorMessage = "Last name cannot exceed 50 characters.")]
        public string LastName { get; set; }

        [Required]
        [EmailAddress]
        [StringLength(100, ErrorMessage = "Email cannot exceed 100 characters.")]
        public string Email { get; set; }

        [Required]
        [MinLength(6, ErrorMessage = "Password must be at least 6 characters long.")]
        public string Password { get; set; }

        public List<string> Roles { get; set; } = new List<string>();
    }
}
UserUpdateDTO

Create a class file named UserUpdateDTO.cs within the DTOs folder and copy and paste the following code. Transfers updated user data from the client to the server, including the ID for identification.

using System.ComponentModel.DataAnnotations;

namespace RoleBasedBasicAuthenticationDemo.DTOs
{
    public class UserUpdateDTO
    {
        [Required]
        public int Id { get; set; }

        [Required]
        [StringLength(50, ErrorMessage = "First name cannot exceed 50 characters.")]
        public string FirstName { get; set; }

        [Required]
        [StringLength(50, ErrorMessage = "Last name cannot exceed 50 characters.")]
        public string LastName { get; set; }

        [Required]
        [EmailAddress]
        [StringLength(100, ErrorMessage = "Email cannot exceed 100 characters.")]
        public string Email { get; set; }

        [Required]
        [MinLength(6, ErrorMessage = "Password must be at least 6 characters long.")]
        public string Password { get; set; }

        public List<string> Roles { get; set; } = new List<string>();
    }
}
ProductCreateDTO

Create a class file named ProductCreateDTO.cs within the DTOs folder and copy and paste the following code. Transfers product creation data from the client to the server, excluding the auto-generated ID.

using System.ComponentModel.DataAnnotations;

namespace RoleBasedBasicAuthenticationDemo.DTOs
{
    public class ProductCreateDTO
    {
        [Required]
        [StringLength(100, ErrorMessage = "Product name cannot exceed 100 characters.")]
        public string Name { get; set; }

        [StringLength(500, ErrorMessage = "Description cannot exceed 500 characters.")]
        public string? Description { get; set; }

        [Required]
        [Range(0.0, double.MaxValue, ErrorMessage = "Price must be a positive value.")]
        public decimal Price { get; set; }

        [Required]
        [Range(0, 1000, ErrorMessage = "Quantiy must be a positive value between 0 and 1000.")]
        public int Quantity { get; set; }
    }
}
ProductReadDTO

Create a class file named ProductReadDTO.cs within the DTOs folder and copy and paste the following code. Transfers product details from the server to the client.

namespace RoleBasedBasicAuthenticationDemo.DTOs
{
    public class ProductReadDTO
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string? Description { get; set; }
        public decimal Price { get; set; }
        public int Quantity { get; set; }
    }
}
ProductUpdateDTO

Create a class file named ProductUpdateDTO.cs within the DTOs folder and copy and paste the following code. Transfers updated product data from the client to the server, including the ID for identification.

using System.ComponentModel.DataAnnotations;

namespace RoleBasedBasicAuthenticationDemo.DTOs
{
    public class ProductUpdateDTO
    {
        [Required]
        public int Id { get; set; }

        [Required]
        [StringLength(100, ErrorMessage = "Product name cannot exceed 100 characters.")]
        public string Name { get; set; }

        [StringLength(500, ErrorMessage = "Description cannot exceed 500 characters.")]
        public string? Description { get; set; }

        [Required]
        [Range(0.0, double.MaxValue, ErrorMessage = "Price must be a positive value.")]
        public decimal Price { get; set; }

        [Required]
        [Range(0, 1000, ErrorMessage = "Quantiy must be a positive value between 0 and 1000.")]
        public int Quantity { get; set; }
    }
}
Database Context:

The Application Db Context class defines the database context, including configurations for relationships and seeding. It manages the following things:

  • Manages interaction with the database using EF Core.
  • Defines DbSet properties for User, Role, UserRole, and Product.
  • Configures relationships (e.g., the composite primary key for UserRole, foreign keys).
  • Seeds initial data for testing and prototyping.

First, create a folder named Data in the project root directory. Then, inside the Data folder, create a class file named ApplicationDbContext.cs and copy and paste the following code.

using Microsoft.EntityFrameworkCore;
using RoleBasedBasicAuthenticationDemo.Models;

namespace RoleBasedBasicAuthenticationDemo.Data
{
    public class ApplicationDbContext : DbContext
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            // Configure a composite primary key for UserRole
            modelBuilder.Entity<UserRole>()
                .HasKey(ur => new { ur.UserId, ur.RoleId });

            // Configure many-to-many relationships between User and Role via UserRole.
            modelBuilder.Entity<UserRole>()
                .HasOne(ur => ur.User)
                .WithMany(u => u.UserRoles)
                .HasForeignKey(ur => ur.UserId);

            modelBuilder.Entity<UserRole>()
                .HasOne(ur => ur.Role)
                .WithMany(r => r.UserRoles)
                .HasForeignKey(ur => ur.RoleId);

            // Seed initial users
            modelBuilder.Entity<User>().HasData(
                new User { Id = 1, FirstName = "John", LastName = "Doe", Email = "john.doe@example.com", Password = "password123" },
                new User { Id = 2, FirstName = "Jane", LastName = "Smith", Email = "jane.smith@example.com", Password = "password123" },
                new User { Id = 3, FirstName = "Hina", LastName = "Sharma", Email = "hina.sharma@example.com", Password = "password123" },
                new User { Id = 4, FirstName = "Sara", LastName = "Taylor", Email = "sara.taylor@example.com", Password = "password123" }
            );

            // Seed initial roles
            modelBuilder.Entity<Role>().HasData(
                new Role { Id = 1, Name = "Admin", Description="Admin Role Description" },
                new Role { Id = 2, Name = "User", Description = "User Role Description" }
            );

            // Seed initial UserRoles
            modelBuilder.Entity<UserRole>().HasData(
                new UserRole { UserId = 1, RoleId = 1 }, // John Doe is Admin
                new UserRole { UserId = 2, RoleId = 2 }, // Jane Smith is User
                new UserRole { UserId = 3, RoleId = 1 }, // Hina Sharma is Admin
                new UserRole { UserId = 3, RoleId = 2 }  // Hina Taylor is also User
            );

            // Optionally, seed initial products
            modelBuilder.Entity<Product>().HasData(
                new Product { Id = 1, Name = "Laptop", Description = "A high-performance laptop.", Price = 1500.00M, Quantity=100 },
                new Product { Id = 2, Name = "Smartphone", Description = "A latest model smartphone.", Price = 800.00M, Quantity = 50 }
            );
        }

        // DbSet Properties Manage entities: Users, Roles, UserRoles, and Products. 
        public DbSet<User> Users { get; set; }
        public DbSet<Role> Roles { get; set; }
        public DbSet<UserRole> UserRoles { get; set; }
        public DbSet<Product> Products { get; set; }
    }
}

Services

Services encapsulate business logic and data access operations. They keep the controllers thin and maintain separation of concerns. So, first, create a folder named Services, where we will create all our services.

IUserService Interface

Create a class file named IUserService.cs within the Services folder and copy and paste the following code. Define all operations related to users, including CRUD operations and role assignment.

using RoleBasedBasicAuthenticationDemo.Models;

namespace RoleBasedBasicAuthenticationDemo.Services
{
    public interface IUserService
    {
        // User CRUD Operations
        Task<IEnumerable<User>> GetUsersAsync();
        Task<User?> GetUserByIdAsync(int id);
        Task<User> CreateUserAsync(User user);
        Task<bool> UpdateUserAsync(User user);
        Task<bool> DeleteUserAsync(int id);
        Task<User?> ValidateUserAsync(string email, string password);

        // Role Management
        Task<IEnumerable<Role>> GetRolesAsync();
        Task AssignRoleToUserAsync(int userId, string roleName);
        Task<IEnumerable<Role>> GetUserRolesAsync(int userId);
    }
}
UserService Implementation

Create a class file named UserService.cs within the Services folder and copy and paste the following code. Implement all operations related to users, including CRUD operations and role assignments.

using RoleBasedBasicAuthenticationDemo.Data;
using RoleBasedBasicAuthenticationDemo.Models;
using Microsoft.EntityFrameworkCore;

namespace RoleBasedBasicAuthenticationDemo.Services
{
    public class UserService : IUserService
    {
        private readonly ApplicationDbContext _context;

        public UserService(ApplicationDbContext context)
        {
            _context = context;
        }

        // User CRUD Operations
        public async Task<IEnumerable<User>> GetUsersAsync()
        {
            return await _context.Users
                .Include(u => u.UserRoles)
                    .ThenInclude(ur => ur.Role)
                .AsNoTracking()
                .ToListAsync();
        }

        public async Task<User?> GetUserByIdAsync(int id)
        {
            return await _context.Users
                .Include(u => u.UserRoles)
                    .ThenInclude(ur => ur.Role)
                .FirstOrDefaultAsync(u => u.Id == id);
        }

        public async Task<User> CreateUserAsync(User user)
        {
            _context.Users.Add(user);
            await _context.SaveChangesAsync();
            return user;
        }

        public async Task<bool> UpdateUserAsync(User user)
        {
            var existingUser = await _context.Users.FindAsync(user.Id);
            if (existingUser == null)
            {
                return false;
            }

            existingUser.FirstName = user.FirstName;
            existingUser.LastName = user.LastName;
            existingUser.Email = user.Email;
            existingUser.Password = user.Password;

            await _context.SaveChangesAsync();
            return true;
        }

        public async Task<bool> DeleteUserAsync(int id)
        {
            var user = await _context.Users.FindAsync(id);
            if (user == null) return false;

            _context.Users.Remove(user);
            await _context.SaveChangesAsync();
            return true;
        }

        public async Task<User?> ValidateUserAsync(string email, string password)
        {
            // Note: In production, passwords should be hashed and salted.
            return await _context.Users
                .Include(u => u.UserRoles)
                    .ThenInclude(ur => ur.Role)
                .FirstOrDefaultAsync(u => u.Email == email && u.Password == password);
        }

        // Role Management
        public async Task<IEnumerable<Role>> GetRolesAsync()
        {
            return await _context.Roles.AsNoTracking().ToListAsync();
        }

        public async Task AssignRoleToUserAsync(int userId, string roleName)
        {
            var user = await _context.Users
                .Include(u => u.UserRoles)
                .FirstOrDefaultAsync(u => u.Id == userId);

            if (user == null)
                throw new ArgumentException("User not found");

            var role = await _context.Roles.FirstOrDefaultAsync(r => r.Name.Equals(roleName, StringComparison.OrdinalIgnoreCase));

            if (role == null)
                throw new ArgumentException("Role not found");

            if (!user.UserRoles.Any(ur => ur.RoleId == role.Id))
            {
                user.UserRoles.Add(new UserRole { UserId = userId, RoleId = role.Id });
                await _context.SaveChangesAsync();
            }
        }

        public async Task<IEnumerable<Role>> GetUserRolesAsync(int userId)
        {
            var user = await _context.Users
                .Include(u => u.UserRoles)
                    .ThenInclude(ur => ur.Role)
                .FirstOrDefaultAsync(u => u.Id == userId);

            return user?.UserRoles.Select(ur => ur.Role) ?? Enumerable.Empty<Role>();
        }
    }
}
IProductService Interface

Create a class file named IProductService.cs within the Services folder and copy and paste the following code. Define all product-related operations, including creating, reading, updating, and deleting.

using RoleBasedBasicAuthenticationDemo.Models;

namespace RoleBasedBasicAuthenticationDemo.Services
{
    public interface IProductService
    {
        Task<IEnumerable<Product>> GetAllProductsAsync();
        Task<Product?> GetProductByIdAsync(int id);
        Task<Product> CreateProductAsync(Product product);
        Task<bool> UpdateProductAsync(Product product);
        Task<bool> DeleteProductAsync(int id);
    }
}
ProductService Implementation

Create a class file named ProductService.cs within the Services folder and copy and paste the following code. Implement all product-related operations, including creating, reading, updating, and deleting.

using RoleBasedBasicAuthenticationDemo.Data;
using RoleBasedBasicAuthenticationDemo.Models;
using Microsoft.EntityFrameworkCore;

namespace RoleBasedBasicAuthenticationDemo.Services
{
    public class ProductService : IProductService
    {
        private readonly ApplicationDbContext _context;

        public ProductService(ApplicationDbContext context)
        {
            _context = context;
        }

        // Retrieve all products
        public async Task<IEnumerable<Product>> GetAllProductsAsync()
        {
            return await _context.Products.AsNoTracking().ToListAsync();
        }

        // Retrieve a product by ID
        public async Task<Product?> GetProductByIdAsync(int id)
        {
            return await _context.Products.FirstOrDefaultAsync(p => p.Id == id);
        }

        // Create a new product
        public async Task<Product> CreateProductAsync(Product product)
        {
            _context.Products.Add(product);
            await _context.SaveChangesAsync();
            return product;
        }

        // Update an existing product
        public async Task<bool> UpdateProductAsync(Product product)
        {
            var existingProduct = await _context.Products.FindAsync(product.Id);
            if (existingProduct == null)
                return false;

            existingProduct.Name = product.Name;
            existingProduct.Description = product.Description;
            existingProduct.Price = product.Price;
            existingProduct.Quantity = product.Quantity;

            await _context.SaveChangesAsync();
            return true;
        }

        // Delete a product by ID
        public async Task<bool> DeleteProductAsync(int id)
        {
            var product = await _context.Products.FindAsync(id);
            if (product == null)
                return false;

            _context.Products.Remove(product);
            await _context.SaveChangesAsync();
            return true;
        }
    }
}
Custom Basic Authentication Handler

First, create a folder named Handler in the project root directory, where we will create our Custom Basic Authentication Handler. The Custom Basic Authentication Handler processes incoming HTTP requests, validates Basic Authentication credentials, and sets up the user’s claims, including role claims. So, create a class file named BasicAuthenticationHandler.cs within the Handler folder and copy and paste the following code.

using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Options;
using RoleBasedBasicAuthenticationDemo.Services;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text.Encodings.Web;
using System.Text;

namespace RoleBasedBasicAuthenticationDemo.Handler
{
    // This class implements a custom Basic Authentication scheme by extending
    // the AuthenticationHandler with AuthenticationSchemeOptions.
    public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
    {
        // Stores a reference to the IUserService, which handles user validation and data access.
        private readonly IUserService _userService;

        // Constructor:
        // - Receives necessary dependencies such as AuthenticationSchemeOptions, ILoggerFactory, and UrlEncoder.
        // - Also takes IUserService to validate user credentials.
        // - Passes the first three parameters to the base constructor of AuthenticationHandler.
        public BasicAuthenticationHandler(
            IOptionsMonitor<AuthenticationSchemeOptions> options,
            ILoggerFactory logger,
            UrlEncoder encoder,
            IUserService userService)
            : base(options, logger, encoder)
        {
            _userService = userService;
        }

        // Main method that handles the authentication process.
        // The framework calls this method when a request requires authentication.
        protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            // Check if the Authorization header is present in the current HTTP request.
            if (!Request.Headers.ContainsKey("Authorization"))
            {
                // If missing, fail the authentication process with a specific error message.
                return AuthenticateResult.Fail("Missing Authorization Header");
            }

            // Retrieve the value of the Authorization header.
            var authorizationHeader = Request.Headers["Authorization"].ToString();

            // Attempt to parse the Authorization header into a recognized format (AuthenticationHeaderValue).
            if (!AuthenticationHeaderValue.TryParse(authorizationHeader, out var headerValue))
            {
                // If it can’t parse the header, fail the authentication.
                return AuthenticateResult.Fail("Invalid Authorization Header");
            }

            // Ensure that the scheme in the header is "Basic".
            // Basic Authentication has the scheme name "Basic" plus a Base64 encoded string.
            if (!"Basic".Equals(headerValue.Scheme, StringComparison.OrdinalIgnoreCase))
            {
                // If the scheme is not Basic, fail.
                return AuthenticateResult.Fail("Invalid Authorization Scheme");
            }

            // Decode the Base64-encoded credentials "<email>:<password>".
            // Split the resulting string on the first ':' to separate email and password.
            var credentials = Encoding.UTF8.GetString(Convert.FromBase64String(headerValue.Parameter)).Split(':', 2);

            // If we don’t have exactly two parts (email and password), the credentials are invalid.
            if (credentials.Length != 2)
            {
                return AuthenticateResult.Fail("Invalid Basic Authentication Credentials");
            }

            // Extract email and password from the decoded credentials array.
            var email = credentials[0];
            var password = credentials[1];

            try
            {
                // Validate the user using the IUserService’s method.
                // This typically checks if the email and password match a record in the database.
                var user = await _userService.ValidateUserAsync(email, password);

                // If the user is not found (null), fail the authentication with an error message.
                if (user == null)
                {
                    return AuthenticateResult.Fail("Invalid Username or Password");
                }

                // Retrieve the roles associated with this user to build role-based claims.
                var roles = user.UserRoles.Select(ur => ur.Role.Name).ToList();

                // Create a list of Claims that will represent the authenticated user’s identity.
                var claims = new List<Claim>
                {
                    // This claim uniquely identifies the user (using their ID).
                    new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),

                    // This claim holds the user's email address.
                    new Claim(ClaimTypes.Name, user.Email)
                };

                // Loop through each user role and add a corresponding role claim.
                foreach (var role in roles)
                {
                    claims.Add(new Claim(ClaimTypes.Role, role));
                }

                // Create a ClaimsIdentity, specifying the authentication scheme name.
                var claimsIdentity = new ClaimsIdentity(claims, Scheme.Name);

                // Create a ClaimsPrincipal that can be checked against policies or role requirements.
                var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);

                // Build the AuthenticationTicket with the principal and scheme name.
                var authenticationTicket = new AuthenticationTicket(claimsPrincipal, Scheme.Name);

                // Indicate a successful authentication by returning AuthenticateResult.Success.
                return AuthenticateResult.Success(authenticationTicket);
            }
            catch
            {
                // If any error occurs during user validation or claims creation,
                // fail the authentication with a general error message.
                return AuthenticateResult.Fail("Error occurred during authentication");
            }
        }

        // This override method is called by the framework when a challenge response is needed.
        // A challenge response indicates the client needs to provide credentials (e.g., 401 Unauthorized).
        protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
        {
            // Adds the "WWW-Authenticate" header with "Basic" so that clients understand they should
            // retry the request with Basic credentials if unauthorized.
            Response.Headers["WWW-Authenticate"] = "Basic";

            // Call the base method to finalize the challenge response.
            await base.HandleChallengeAsync(properties);
        }
    }
}
Modify appsettings.json

Make sure to add a connection string to your appsettings.json file. So, please modify the appsettings.json file as follows.

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "EFCoreDBConnection": "Server=LAPTOP-6P5NK25R\\SQLSERVER2022DEV; Database=ECommerceDB; Trusted_Connection=True; TrustServerCertificate=True;"
  }
}
Application Configuration Setup

Adds services to the DI container (e.g., ApplicationDbContext, UserService, ProductService). Configures Basic authentication and authorization, defining custom policies. Maps controllers and sets up middleware for authentication, authorization, and HTTPS redirection. So, please modify the Program class as follows.

using Microsoft.AspNetCore.Authentication;
using Microsoft.EntityFrameworkCore;
using RoleBasedBasicAuthenticationDemo.Data;
using RoleBasedBasicAuthenticationDemo.Handler;
using RoleBasedBasicAuthenticationDemo.Services;

namespace RoleBasedBasicAuthenticationDemo
{
    public class Program
    {
        public static void Main(string[] args)
        {
            // Create a WebApplicationBuilder instance, which sets up default services,
            // configuration, environment detection, and more.
            var builder = WebApplication.CreateBuilder(args);

            // Add services to the container.

            // Add controller services and configure JSON options.
            builder.Services.AddControllers()
                .AddJsonOptions(options =>
                {
                    // Disable automatic camel casing so property names remain as defined.
                    options.JsonSerializerOptions.PropertyNamingPolicy = null;
                });

            // Enable API documentation using Swagger/OpenAPI.
            builder.Services.AddEndpointsApiExplorer();
            builder.Services.AddSwaggerGen();

            // Configure Entity Framework Core to use SQL Server.
            // We retrieve the connection string from appsettings.json (key: "EFCoreDBConnection").
            // Use SQL Server with the connection string from the configuration file.
            builder.Services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(builder.Configuration.GetConnectionString("EFCoreDBConnection")));

            // Register the IUserService and its implementation (UserService) for dependency injection.
            // The service will be created once per HTTP request.
            builder.Services.AddScoped<IUserService, UserService>();

            // Register the IProductService and its implementation (ProductService) for dependency injection.
            builder.Services.AddScoped<IProductService, ProductService>();

            // Configure authentication to use our custom BasicAuthenticationHandler.
            // "BasicAuthentication" is the scheme name; the handler is specified as BasicAuthenticationHandler.
            builder.Services.AddAuthentication("BasicAuthentication")
                .AddScheme<AuthenticationSchemeOptions, BasicAuthenticationHandler>("BasicAuthentication", null);

            // Configure authorization and define role-based policies:
            //   - "AdminOnly": Requires the "Admin" role.
            //   - "UserOnly": Requires the "User" role.
            //   - "AdminOrUser": Requires either "Admin" or "User" role.
            //   - "AdminAndUser": Requires both "Admin" AND "User" roles (via a custom assertion).
            builder.Services.AddAuthorization(options =>
            {
                options.AddPolicy("AdminOnly", policy => policy.RequireRole("Admin"));
                options.AddPolicy("UserOnly", policy => policy.RequireRole("User"));
                options.AddPolicy("AdminOrUser", policy => policy.RequireRole("Admin", "User"));
                options.AddPolicy("AdminAndUser", policy => policy.RequireAssertion(context =>
                    context.User.IsInRole("Admin") && context.User.IsInRole("User")));
            });

            // Build the WebApplication. This finalizes service registrations and configuration.
            var app = builder.Build();

            // Configure the HTTP request pipeline.
            if (app.Environment.IsDevelopment())
            {
                // In development mode, enable Swagger UI for exploring and testing the API.
                app.UseSwagger();
                app.UseSwaggerUI();
            }

            // Enforce HTTPS redirection (HTTP requests will be redirected to HTTPS).
            app.UseHttpsRedirection();

            // Use the authentication and authorization middleware:
            //   - app.UseAuthentication() attempts to authenticate the request.
            //   - app.UseAuthorization() enforces any policies (roles, claims) on the endpoints.
            app.UseAuthentication();
            app.UseAuthorization();

            // Map controller endpoints. 
            // This tells ASP.NET Core to route incoming requests to the respective controller actions.
            app.MapControllers();

            // Run the application, starting the server and processing incoming requests.
            app.Run();
        }
    }
}
Database Migration

Next, we need to generate the Migration and update the database schema. So, open the Package Manager Console and Execute the Add-Migration and Update-Database commands as follows.

Role-Based Basic Authentication in ASP.NET Core Web API

With this. our Database with Users tables should be created as shown in the image below:

how to implement Role-Based Basic Authentication in ASP.NET Core Web API

Creating Controllers

We will create two controllers:

  • UserController: Manages user-related operations.
  • ProductsController: Manages product-related operations with role-based access.
UsersController

Create an API empty controller named UsersController within the Controllers folder and then copy and paste the following code:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using RoleBasedBasicAuthenticationDemo.DTOs;
using RoleBasedBasicAuthenticationDemo.Models;
using RoleBasedBasicAuthenticationDemo.Services;

namespace RoleBasedBasicAuthenticationDemo.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    [Authorize(AuthenticationSchemes = "BasicAuthentication")]
    public class UserController : ControllerBase
    {
        private readonly IUserService _userService;

        public UserController(IUserService userService)
        {
            _userService = userService;
        }

        // GET: api/User/GetAllUsersAsync
        [HttpGet("GetAllUsersAsync")]
        [Authorize(Policy = "AdminOnly")] // Only Admin can view all users
        public async Task<ActionResult<IEnumerable<UserReadDTO>>> GetAllUsersAsync()
        {
            var users = await _userService.GetUsersAsync();

            var userDtos = users.Select(u => new UserReadDTO
            {
                Id = u.Id,
                FirstName = u.FirstName,
                LastName = u.LastName,
                Email = u.Email,
                // Password is excluded from the response for security
                Roles = u.UserRoles.Select(ur => ur.Role.Name).ToList()
            }).ToList();

            return Ok(userDtos);
        }

        // GET: api/User/GetUserByIdAsync/1
        [HttpGet("GetUserByIdAsync/{id}")]
        [Authorize(Policy = "AdminOrUser")] // Admin and User can view individual user
        public async Task<ActionResult<UserReadDTO>> GetUserByIdAsync(int id)
        {
            var user = await _userService.GetUserByIdAsync(id);
            if (user == null)
                return NotFound();

            var userDto = new UserReadDTO
            {
                Id = user.Id,
                FirstName = user.FirstName,
                LastName = user.LastName,
                Email = user.Email,
                Roles = user.UserRoles.Select(ur => ur.Role.Name).ToList()
            };

            return Ok(userDto);
        }

        // POST: api/User/CreateUserAsync
        [HttpPost("CreateUserAsync")]
        [Authorize(Policy = "AdminOnly")] // Only Admin can create users
        public async Task<ActionResult<UserReadDTO>> CreateUserAsync([FromBody] UserCreateDTO userDto)
        {
            if (!ModelState.IsValid)
                return BadRequest(ModelState);

            // Map DTO -> Entity
            var user = new User
            {
                FirstName = userDto.FirstName,
                LastName = userDto.LastName,
                Email = userDto.Email,
                Password = userDto.Password
            };

            user = await _userService.CreateUserAsync(user);

            // Assign roles if any
            foreach (var role in userDto.Roles)
            {
                await _userService.AssignRoleToUserAsync(user.Id, role);
            }

            // Map Entity -> DTO
            var createdUser = await _userService.GetUserByIdAsync(user.Id);
            var userReadDto = new UserReadDTO
            {
                Id = createdUser.Id,
                FirstName = createdUser.FirstName,
                LastName = createdUser.LastName,
                Email = createdUser.Email,
                Roles = createdUser.UserRoles.Select(ur => ur.Role.Name).ToList()
            };

            return CreatedAtAction(nameof(GetUserByIdAsync), new { id = user.Id }, userReadDto);
        }

        // PUT: api/User/UpdateUserAsync/1
        [HttpPut("UpdateUserAsync/{id}")]
        [Authorize(Policy = "AdminOnly")] // Only Admin can update users
        public async Task<IActionResult> UpdateUserAsync(int id, [FromBody] UserUpdateDTO userDto)
        {
            if (!ModelState.IsValid)
                return BadRequest(ModelState);

            if (id != userDto.Id)
                return BadRequest("ID in URL doesn't match ID in payload.");

            // Map DTO -> Entity
            var user = new User
            {
                Id = userDto.Id,
                FirstName = userDto.FirstName,
                LastName = userDto.LastName,
                Email = userDto.Email,
                Password = userDto.Password
            };

            var updated = await _userService.UpdateUserAsync(user);
            if (!updated)
                return NotFound();

            // Update roles
            // For simplicity, remove all existing roles and assign new ones
            // In production, consider more sophisticated role management
            var existingRoles = await _userService.GetUserRolesAsync(user.Id);
            var rolesToRemove = existingRoles.Select(r => r.Name).Except(userDto.Roles).ToList();
            var rolesToAdd = userDto.Roles.Except(existingRoles.Select(r => r.Name)).ToList();

            // Note: Role removal not implemented; implement if necessary
            foreach (var role in rolesToAdd)
            {
                await _userService.AssignRoleToUserAsync(user.Id, role);
            }

            return NoContent();
        }

        // DELETE: api/User/DeleteUserAsync/1
        [HttpDelete("DeleteUserAsync/{id}")]
        [Authorize(Policy = "UserOnly")] // Only Admin can delete users
        public async Task<IActionResult> DeleteUserAsync(int id)
        {
            var deleted = await _userService.DeleteUserAsync(id);
            if (!deleted)
                return NotFound();

            return NoContent();
        }

        // POST: api/User/1/assign-role
        [HttpPost("{id}/assign-role")]
        [Authorize(Policy = "AdminOnly")] // Only Admin can assign roles
        public async Task<IActionResult> AssignRoleToUserAsync(int id, [FromBody] string roleName)
        {
            try
            {
                await _userService.AssignRoleToUserAsync(id, roleName);
                return Ok($"Role '{roleName}' assigned to user with ID {id}.");
            }
            catch (ArgumentException ex)
            {
                return BadRequest(ex.Message);
            }
        }
    }
}
ProductsController

Create an API empty controller named ProductsController within the Controllers folder and then copy and paste the following code:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using RoleBasedBasicAuthenticationDemo.Models;
using RoleBasedBasicAuthenticationDemo.DTOs;
using RoleBasedBasicAuthenticationDemo.Services;

namespace RoleBasedBasicAuthenticationDemo.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    [Authorize(AuthenticationSchemes = "BasicAuthentication")]
    public class ProductsController : ControllerBase
    {
        private readonly IProductService _productService;

        public ProductsController(IProductService productService)
        {
            _productService = productService;
        }

        // GET: api/Products/GetAllProductsAsync
        [HttpGet("GetAllProductsAsync")]
        [Authorize(Policy = "AdminOrUser")] // Accessible by Admin or User
        public async Task<ActionResult<IEnumerable<ProductReadDTO>>> GetAllProductsAsync()
        {
            var products = await _productService.GetAllProductsAsync();

            var productDtos = products.Select(p => new ProductReadDTO
            {
                Id = p.Id,
                Name = p.Name,
                Description = p.Description,
                Price = p.Price,
                Quantity = p.Quantity,
            }).ToList();

            return Ok(productDtos);
        }

        // GET: api/Products/GetProductByIdAsync/1
        [HttpGet("GetProductByIdAsync/{id}")]
        [Authorize(Policy = "UserOnly")] // Accessible by User only
        public async Task<ActionResult<ProductReadDTO>> GetProductByIdAsync(int id)
        {
            var product = await _productService.GetProductByIdAsync(id);
            if (product == null)
                return NotFound();

            var productDto = new ProductReadDTO
            {
                Id = product.Id,
                Name = product.Name,
                Description = product.Description,
                Price = product.Price,
                Quantity = product.Quantity,
            };

            return Ok(productDto);
        }

        // POST: api/Products/CreateProductAsync
        [HttpPost("CreateProductAsync")]
        [Authorize(Policy = "AdminOnly")] // Only Admin can create products
        public async Task<ActionResult<ProductReadDTO>> CreateProductAsync([FromBody] ProductCreateDTO productCreateDto)
        {
            if (!ModelState.IsValid)
                return BadRequest(ModelState);

            // Map DTO to Entity
            var product = new Product
            {
                Name = productCreateDto.Name,
                Description = productCreateDto.Description,
                Price = productCreateDto.Price,
                Quantity = productCreateDto.Quantity,
            };

            product = await _productService.CreateProductAsync(product);

            // Map Entity to Read DTO
            var productReadDto = new ProductReadDTO
            {
                Id = product.Id,
                Name = product.Name,
                Description = product.Description,
                Price = product.Price,
                Quantity = product.Quantity,
            };

            return Ok(productReadDto);
        }

        // PUT: api/Products/UpdateProductAsync/1
        [HttpPut("UpdateProductAsync/{id}")]
        [Authorize(Policy = "AdminOrUser")] // Accessible by Admin or User
        public async Task<IActionResult> UpdateProductAsync(int id, [FromBody] ProductUpdateDTO productUpdateDto)
        {
            if (!ModelState.IsValid)
                return BadRequest(ModelState);

            if (id != productUpdateDto.Id)
                return BadRequest("Product ID mismatch.");

            // Map DTO to Entity
            var product = new Product
            {
                Id = productUpdateDto.Id,
                Name = productUpdateDto.Name,
                Description = productUpdateDto.Description,
                Price = productUpdateDto.Price,
                Quantity = productUpdateDto.Quantity,
            };

            var updated = await _productService.UpdateProductAsync(product);
            if (!updated)
                return NotFound();

            return NoContent();
        }

        // DELETE: api/Products/DeleteProductAsync/1
        [HttpDelete("DeleteProductAsync/{id}")]
        [Authorize(Policy = "AdminAndUser")] // Accessible by user having both Admin and User Role
        public async Task<IActionResult> DeleteProductAsync(int id)
        {
            var deleted = await _productService.DeleteProductAsync(id);
            if (!deleted)
                return NotFound();

            return NoContent();
        }
    }
}
Testing the Application

With this, our server-side setup is completed. You can test the application using tools like Swagger, Postman, Fiddler, etc. However, we will create a .NET Console application as the client application to test the services.

Creating Client Application:

To demonstrate role-based access, we will use a .NET Console Application to consume the Web API. So, create a new .NET Console Application named BasicAuthConsoleClient that will consume the Web API services with Role-Based Basic Authentication.

Once you create the Console application, please modify the Program class as follows. The following code is self-explained, so please read the comment lines for a better understanding.

using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;

namespace BasicAuthConsoleClient
{
    public class ProductCreateDTO
    {
        public string Name { get; set; }
        public string? Description { get; set; }
        public decimal Price { get; set; }
        public int Quantity { get; set; }
    }

    public class ProductReadDTO
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string? Description { get; set; }
        public decimal Price { get; set; }
        public int Quantity { get; set; }
    }

    public class ProductUpdateDTO
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string? Description { get; set; }
        public decimal Price { get; set; }
        public int Quantity { get; set; }
    }

    public class Program
    {
        // Change baseUrl to match your ASP.NET Core Web API address/port.
        private static readonly string baseUrl = "https://localhost:7183"; // Update PORT as necessary

        private static async Task Main(string[] args)
        {
            Console.WriteLine("Basic Auth Console Client with Role-Based Authorization");

            //"john.doe@example.com", "password123", "Admin"
            //"jane.smith@example.com", "password123", "User"
            //"hina.sharma@example.com", "password123", "Admin and User"
            //"sara.taylor@example.com", "password123", "No Roles"
            var username = "hina.sharma@example.com";
            var password = "password123";

            // Create and configure HttpClient
            using var httpClient = new HttpClient();

            // Attach Basic Authentication header
            var authToken = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}:{password}"));
            httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authToken);

            Console.WriteLine($"\nAuthenticated as: {username} ");

            // 1. GET all products
            await GetAllProductsAsync(httpClient);

            // 2. GET a single product by Id (e.g., Id = 1)
            await GetProductByIdAsync(httpClient, 1);

            // 3. CREATE (POST) a new product
            var newProductId = await CreateProductAsync(httpClient);

            // 4. UPDATE (PUT) the new product
            if (newProductId > 0)
                await UpdateProductAsync(httpClient, newProductId);

            // 5. DELETE the product
            if (newProductId > 0)
                await DeleteProductAsync(httpClient, newProductId);

            Console.WriteLine("\nAll Operations are Completed");
            Console.ReadKey();
        }

        private static async Task GetAllProductsAsync(HttpClient httpClient)
        {
            Console.WriteLine("\n--- GET ALL PRODUCTS ---");
            try
            {
                var response = await httpClient.GetAsync($"{baseUrl}/api/Products/GetAllProductsAsync");
                if (response.StatusCode == System.Net.HttpStatusCode.Forbidden)
                {
                    Console.WriteLine("Access Forbidden: You do not have permission to view products.");
                    return;
                }

                if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
                {
                    Console.WriteLine("Unauthorized: Invalid Credenials");
                    return;
                }

                response.EnsureSuccessStatusCode();

                var responseBody = await response.Content.ReadAsStringAsync();
                var products = JsonSerializer.Deserialize<List<ProductReadDTO>>(responseBody, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });

                Console.WriteLine("Products:");
                if (products != null)
                {
                    foreach (var product in products)
                    {
                        Console.WriteLine($"Id: {product.Id}, Name: {product.Name}, Price: {product.Price}");
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error fetching products: {ex.Message}");
            }
        }

        private static async Task GetProductByIdAsync(HttpClient httpClient, int id)
        {
            Console.WriteLine($"\n--- GET PRODUCT BY ID: {id} ---");
            try
            {
                var response = await httpClient.GetAsync($"{baseUrl}/api/Products/GetProductByIdAsync/{id}");
                if (response.StatusCode == System.Net.HttpStatusCode.Forbidden)
                {
                    Console.WriteLine("Access Forbidden: You do not have permission to view this product.");
                    return;
                }

                if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
                {
                    Console.WriteLine("Unauthorized: Invalid Credenials");
                    return;
                }

                if (!response.IsSuccessStatusCode)
                {
                    Console.WriteLine($"Failed to get product. HTTP Status: {response.StatusCode}");
                    return;
                }

                var responseBody = await response.Content.ReadAsStringAsync();
                var product = JsonSerializer.Deserialize<ProductReadDTO>(responseBody, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });

                if (product != null)
                {
                    Console.WriteLine($"Product Found - Id: {product.Id}, Name: {product.Name}, Price: {product.Price}");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error fetching product by Id: {ex.Message}");
            }
        }

        private static async Task<int> CreateProductAsync(HttpClient httpClient)
        {
            Console.WriteLine("\n--- CREATE NEW PRODUCT (POST) ---");
            var newProduct = new ProductCreateDTO
            {
                Name = $"Product_{Guid.NewGuid()}",
                Description = "A new test product.",
                Price = 99.99M,
                Quantity = 100
            };

            try
            {
                var payload = JsonSerializer.Serialize(newProduct);
                var content = new StringContent(payload, Encoding.UTF8, "application/json");

                var response = await httpClient.PostAsync($"{baseUrl}/api/Products/CreateProductAsync", content);
                if (response.StatusCode == System.Net.HttpStatusCode.Forbidden)
                {
                    Console.WriteLine("Access Forbidden: You do not have permission to create products.");
                    return 0;
                }

                if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
                {
                    Console.WriteLine("Unauthorized: Invalid Credenials");
                    return 0;
                }

                if (!response.IsSuccessStatusCode)
                {
                    Console.WriteLine($"Failed to create product. HTTP Status: {response.StatusCode}");
                    return 0;
                }

                var responseBody = await response.Content.ReadAsStringAsync();
                var createdProduct = JsonSerializer.Deserialize<ProductReadDTO>(responseBody, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });

                if (createdProduct != null)
                {
                    Console.WriteLine($"Created product Id: {createdProduct.Id}, Name: {createdProduct.Name}");
                    return createdProduct.Id;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error creating product: {ex.Message}");
            }

            return 0;
        }

        private static async Task UpdateProductAsync(HttpClient httpClient, int productId)
        {
            Console.WriteLine("\n--- UPDATE PRODUCT (PUT) ---");
            var updatedProduct = new ProductUpdateDTO
            {
                Id = productId,
                Name = $"Updated_Product_{Guid.NewGuid()}",
                Description = "An updated test product.",
                Price = 149.99M,
                Quantity =10
            };

            try
            {
                var payload = JsonSerializer.Serialize(updatedProduct);
                var content = new StringContent(payload, Encoding.UTF8, "application/json");

                var response = await httpClient.PutAsync($"{baseUrl}/api/Products/UpdateProductAsync/{productId}", content);
                if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
                {
                    Console.WriteLine("Unauthorized: Invalid Credenials");
                    return;
                }

                if (response.StatusCode == System.Net.HttpStatusCode.Forbidden)
                {
                    Console.WriteLine("Access Forbidden: You do not have permission to update products.");
                    return;
                }

                if (!response.IsSuccessStatusCode)
                {
                    Console.WriteLine($"Failed to update product. HTTP Status: {response.StatusCode}");
                    return;
                }

                Console.WriteLine("Product successfully updated.");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error updating product: {ex.Message}");
            }
        }

        private static async Task DeleteProductAsync(HttpClient httpClient, int productId)
        {
            Console.WriteLine("\n--- DELETE PRODUCT ---");
            try
            {
                var response = await httpClient.DeleteAsync($"{baseUrl}/api/Products/DeleteProductAsync/{productId}");
                if (response.StatusCode == System.Net.HttpStatusCode.Forbidden)
                {
                    Console.WriteLine("Access Forbidden: You do not have permission to delete products.");
                    return;
                }
                if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
                {
                    Console.WriteLine("Unauthorized: Invalid Credenials");
                    return;
                }

                if (!response.IsSuccessStatusCode)
                {
                    Console.WriteLine($"Failed to delete product. HTTP Status: {response.StatusCode}");
                    return;
                }

                Console.WriteLine($"Product with Id: {productId} deleted successfully.");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error deleting product: {ex.Message}");
            }
        }
    }
}
Testing the Application:

First, run the ASP.NET Core Web API Application and get the Port number on which the Web API Application is running. Then, please update the port number in the console application, where we set the base URL and run the console application. Now, you can run the application with different users with different roles and see whether the Role-Based Basic Authentication works as expected.

Role-Based Basic Authentication in ASP.NET Core Web API

Better Alternatives of Basic Authentication in ASP.NET Core Web API

Token-Based Authentication: Modern applications often use token-based authentication (such as JWT), which can encapsulate user identity and the roles in a secure token, providing better security.

In the next article, I will discuss implementing CORS in ASP.NET Core Web API Application. In this article, I explain Role-Based Basic Authentication in an ASP.NET Core Web API Application with an Example. I hope you enjoy this ASP.NET Core Web API Role-Based Basic Authentication article.

Leave a Reply

Your email address will not be published. Required fields are marked *