Back to: ASP.NET Core Web API Tutorials
How to Implement Basic Authentication in ASP.NET Core Web API
In this article, I will discuss how to implement Basic Authentication in ASP.NET Core Web API with an example. Please read our previous article, which discusses the basic concepts of Authentication and Authorization in Web APIs. Securing web APIs is an essential part of building modern applications. Basic Authentication is one of the most straightforward methods for restricting access to HTTP services, including ASP.NET Core Web API.
What is Basic Authentication?
Basic Authentication is one of the simplest and most straightforward methods for securing web APIs and other HTTP services. It works by requiring the client (such as a web browser, mobile app, or other API consumers) to send a username and password with every request. This username and password pair are used to verify the client’s identity before granting access to the requested resource.
How Does Basic Authentication Work?
Let us understand how basic authentication works. For a better understanding, please refer to the following diagram.
Client Sends Credentials with Each Request:
Every time the client wants to access a protected API endpoint, it includes an Authorization header in the HTTP request. This header contains the username and password combined into a single string, encoded using Base64.
Encoding Credentials:
- The client first concatenates the username and password into a single string, separated by a colon (:). For example: Username:Password
- Suppose the username is john.doe@example.com and the password is password123, the combined string is: john.doe@example.com:password123
- This string is then converted into Base64 encoding, which might look like: YWxhZGRpbjpvcGVuc2VzYW1l
Base64 encoding is a method of converting binary data into a text format; it is not encryption and does not protect the data from being read.
Authorization Header Format:
The encoded string is placed in the HTTP Authorization header, prefixed by the word Basic followed by a space: Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l
Server-Side Validation:
When the API server (e.g., an ASP.NET Core Web API application) receives a request with the Authorization header, it:
- Extracts the Base64 encoded string from the header.
- Decodes it to retrieve the original “username:password” string.
- Splits the string to get the username and password separately.
- Validates these credentials by checking them against a user store, such as a database or in-memory collection.
- If the credentials are valid, the server processes the request and returns the protected resource.
- If invalid, the server returns an HTTP 401 Unauthorized response.
Implementing Basic Authentication in ASP.NET Core Web API:
Create a new ASP.NET Core Web API Project with the name BasicAuthenticationDemo. While creating the project, select No Authentication (we will implement Basic Auth manually). Once you create the project, please install the Entity Framework Core packages by executing the following commands in the Package Manager Console:
- Install-Package Microsoft.EntityFrameworkCore.SqlServer
- Install-Package Microsoft.EntityFrameworkCore.Tools
Create the User Model:
Within the Project root directory, create a folder named Models. Next, create a class file named User.cs within the Models folder and copy and paste the following code. This will be the model that will hold the user data.
using Microsoft.EntityFrameworkCore; using System.ComponentModel.DataAnnotations; namespace BasicAuthenticationDemo.Models { [Index(nameof(Email), Name = "Index_Email_Unique", IsUnique = true)] public class User { public int Id { get; set; } [Required(ErrorMessage = "Full Name is required.")] [StringLength(100, MinimumLength = 3, ErrorMessage = "Full Name must be between 3 and 100 characters.")] public string FullName { get; set; } = null!; [Required(ErrorMessage = "Email is required.")] [EmailAddress(ErrorMessage = "Email format is invalid.")] public string Email { get; set; } = null!; [Required(ErrorMessage = "Password hash is required.")] public string PasswordHash { get; set; } = null!; [Required(ErrorMessage = "Role is required.")] [StringLength(50, ErrorMessage = "Role cannot exceed 50 characters.")] public string Role { get; set; } = null!; } }
Product Entity
Create a class file named Product.cs in the Models folder and then copy and paste the following code.
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace BasicAuthenticationDemo.Models { public class Product { public int Id { get; set; } [Required(ErrorMessage = "Product Name is required.")] [StringLength(150, MinimumLength = 2, ErrorMessage = "Product Name must be between 2 and 150 characters.")] public string Name { get; set; } = null!; [Required(ErrorMessage = "Description is required.")] [StringLength(500, ErrorMessage = "Description cannot exceed 500 characters.")] public string Description { get; set; } = null!; [Column(TypeName = "decimal(12,2)")] [Range(0.01, 9999999999.99, ErrorMessage = "Price must be greater than 0.")] public decimal Price { get; set; } [Range(0, int.MaxValue, ErrorMessage = "Stock cannot be negative.")] public int Stock { get; set; } } }
Create Password Hasher:
Create a class file named PasswordHasher.cs in the Models folder and then copy and paste the following code. The following static helper class is used to handle password hashing and verification using the SHA-256 algorithm.
using System.Security.Cryptography; using System.Text; namespace BasicAuthenticationDemo.Models { public static class PasswordHasher { // Hashes a plain text password into a Base64-encoded SHA-256 hash. public static string HashPassword(string password) { // Create a new instance of SHA256 hashing algorithm. using var sha256 = SHA256.Create(); // Convert the input password string into a byte array using UTF-8 encoding. var bytes = Encoding.UTF8.GetBytes(password); // Compute the SHA-256 hash of the password bytes. var hash = sha256.ComputeHash(bytes); // Convert the hashed byte array into a Base64-encoded string to store or compare easily. return Convert.ToBase64String(hash); } // Verifies whether the provided plain password matches the stored hashed password. public static bool VerifyPassword(string hashedPassword, string providedPassword) { // Hash the provided plain password and compare it with the stored hash. // Returns true if both hashes match, indicating the password is correct. return hashedPassword == HashPassword(providedPassword); } } }
Creating Db Context:
Create a class file named ApplicationDbContext.cs in the Models folder and then copy and paste the following code.
using Microsoft.EntityFrameworkCore; namespace BasicAuthenticationDemo.Models { public class ApplicationDbContext : DbContext { public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { // Seed User Data modelBuilder.Entity<User>().HasData( new User { Id = 1, FullName = "Pranaya Rout", Email = "pranaya.rout@example.com", PasswordHash = PasswordHasher.HashPassword("Pranaya@123"), Role = "Administrator,Manager" }, new User { Id = 2, FullName = "John Doe", Email = "john.doe@example.com", PasswordHash = PasswordHasher.HashPassword("John@123"), Role = "Administrator" }, new User { Id = 3, FullName = "Jane Smith", Email = "jane.smith@example.com", PasswordHash = PasswordHasher.HashPassword("Jane@123"), Role = "Manager" } ); // Seed Product Data modelBuilder.Entity<Product>().HasData( new Product { Id = 1, Name = "Laptop", Description = "High performance laptop", Price = 1200.00m, Stock = 15 }, new Product { Id = 2, Name = "Wireless Mouse", Description = "Ergonomic wireless mouse", Price = 25.99m, Stock = 100 }, new Product { Id = 3, Name = "Mechanical Keyboard", Description = "RGB backlit mechanical keyboard", Price = 79.99m, Stock = 45 } ); } public DbSet<User> Users { get; set; } = null!; public DbSet<Product> Products { get; set; } = null!; } }
Create DTOs for Product
Create a class file named ProductDTO.cs within the Models folder and then copy and paste the following code.
using System.ComponentModel.DataAnnotations; namespace BasicAuthenticationDemo.Models { public class ProductResponseDTO { public int Id { get; set; } public string Name { get; set; } = null!; public string Description { get; set; } = null!; public decimal Price { get; set; } public int Stock { get; set; } } public class CreateProductDTO { [Required(ErrorMessage = "Product Name is required.")] [StringLength(150, MinimumLength = 2, ErrorMessage = "Product Name must be between 2 and 150 characters.")] public string Name { get; set; } = null!; [Required(ErrorMessage = "Description is required.")] [StringLength(500, ErrorMessage = "Description cannot exceed 500 characters.")] public string Description { get; set; } = null!; [Range(0.01, 9999999999.99, ErrorMessage = "Price must be greater than 0.")] public decimal Price { get; set; } [Range(0, int.MaxValue, ErrorMessage = "Stock cannot be negative.")] public int Stock { get; set; } } public class UpdateProductDTO { [Required(ErrorMessage = "Product Id is required.")] public int Id { get; set; } [StringLength(150, MinimumLength = 2, ErrorMessage = "Product Name must be between 2 and 150 characters.")] public string? Name { get; set; } [StringLength(500, ErrorMessage = "Description cannot exceed 500 characters.")] public string? Description { get; set; } [Range(0.01, 9999999999.99, ErrorMessage = "Price must be greater than 0.")] public decimal? Price { get; set; } [Range(0, int.MaxValue, ErrorMessage = "Stock cannot be negative.")] public int? Stock { get; set; } } }
Create Product Service:
The Product Service will provide methods to perform CRUD Operations on the Product entity in the database.
Create an interface
Create a class file named IProductService.cs within the Models folder and then copy and paste the following code.
namespace BasicAuthenticationDemo.Models { public interface IProductService { Task<List<ProductResponseDTO>> GetAllAsync(); Task<ProductResponseDTO?> GetByIdAsync(int id); Task<ProductResponseDTO> CreateAsync(CreateProductDTO dto); Task<bool> UpdateAsync(int id, UpdateProductDTO dto); Task<bool> DeleteAsync(int id); } }
Implement it:
Create a class file named ProductService.cs within the Models folder and then copy and paste the following code.
using Microsoft.EntityFrameworkCore; namespace BasicAuthenticationDemo.Models { public class ProductService : IProductService { private readonly ApplicationDbContext _context; public ProductService(ApplicationDbContext context) { _context = context; } // Mapping Product -> ProductDto private static ProductResponseDTO MapToDto(Product product) { return new ProductResponseDTO { Id = product.Id, Name = product.Name, Description = product.Description, Price = product.Price, Stock = product.Stock }; } public async Task<List<ProductResponseDTO>> GetAllAsync() { var products = await _context.Products.ToListAsync(); return products.Select(MapToDto).ToList(); } public async Task<ProductResponseDTO?> GetByIdAsync(int id) { var product = await _context.Products.FindAsync(id); return product == null ? null : MapToDto(product); } public async Task<ProductResponseDTO> CreateAsync(CreateProductDTO dto) { var product = new Product { Name = dto.Name, Description = dto.Description, Price = dto.Price, Stock = dto.Stock }; _context.Products.Add(product); await _context.SaveChangesAsync(); return MapToDto(product); } public async Task<bool> UpdateAsync(int id, UpdateProductDTO dto) { var product = await _context.Products.FindAsync(id); if (product == null) return false; if (dto.Name != null) product.Name = dto.Name; if (dto.Description != null) product.Description = dto.Description; if (dto.Price.HasValue) product.Price = dto.Price.Value; if (dto.Stock.HasValue) product.Stock = dto.Stock.Value; await _context.SaveChangesAsync(); return true; } public async Task<bool> DeleteAsync(int id) { var product = await _context.Products.FindAsync(id); if (product == null) return false; _context.Products.Remove(product); await _context.SaveChangesAsync(); return true; } } }
Create a Custom Basic Authentication Handler
In ASP.NET Core, we can create a custom Authentication Handler to handle Basic Authentication. So, create a class file named BasicAuthenticationHandler.cs within the Models folder and then copy and paste the following code:
using Microsoft.AspNetCore.Authentication; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; using System.Net.Http.Headers; using System.Security.Claims; using System.Text; using System.Text.Encodings.Web; namespace BasicAuthenticationDemo.Models { // Custom authentication handler that processes Basic Authentication. // Inherits from AuthenticationHandler<TOptions> where TOptions is AuthenticationSchemeOptions. public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions> { // Database context to query user information during authentication. private readonly ApplicationDbContext _context; // Constructor injects dependencies required by the base handler and adds DbContext. // parameters: // - IOptionsMonitor<AuthenticationSchemeOptions> options: Monitors changes to authentication scheme options // - ILoggerFactory logger: Factory to create logger instances // - UrlEncoder encoder: Encodes URLs to ensure they are safe // - ApplicationDbContext context: The context used to validate user credentials. public BasicAuthenticationHandler( IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ApplicationDbContext context) : base(options, logger, encoder) { // Assign the provided ApplicationDbContext to the private field _context = context; } // Core method that performs authentication logic on each request. // Called automatically when ASP.NET Core needs to authenticate a request. protected override async Task<AuthenticateResult> HandleAuthenticateAsync() { try { // Step 1: Check if the Authorization header exists in the request. if (!Request.Headers.ContainsKey("Authorization")) { // No credentials provided, authentication fails. return AuthenticateResult.Fail("Missing Authorization Header"); } // Step 2: Retrieve the Authorization header value. var authorizationHeader = Request.Headers["Authorization"].ToString(); // Step 3: Parse the header to separate scheme and parameter. // The expected format is: "Basic base64encodedCredentials" if (!AuthenticationHeaderValue.TryParse(authorizationHeader, out var headerValue)) { // If parsing fails, the header is considered invalid and authentication fails. return AuthenticateResult.Fail("Invalid Authorization Header"); } // Step 4: Verify that the scheme is "Basic" (case-insensitive). if (!"Basic".Equals(headerValue.Scheme, StringComparison.OrdinalIgnoreCase)) { // If not Basic scheme, authentication is not applicable. return AuthenticateResult.Fail("Invalid Authorization Scheme"); } // Step 5: Decode the Base64-encoded credentials ("username:password"). var credentialBytes = Convert.FromBase64String(headerValue.Parameter!); var credentials = Encoding.UTF8.GetString(credentialBytes).Split(':', 2); // Step 6: Validate that the decoded string contains exactly username and password. if (credentials.Length != 2) { // If not, the credentials are invalid and authentication fails. return AuthenticateResult.Fail("Invalid Authorization Header"); } // Step 7: Extract username (here Email) and password from credentials. var email = credentials[0]; var password = credentials[1]; // Step 8: Query the database for the user by email. var user = await _context.Users.SingleOrDefaultAsync(u => u.Email == email); if (user == null || !PasswordHasher.VerifyPassword(user.PasswordHash, password)) { // User not found or password incorrect. return AuthenticateResult.Fail("Invalid Username or Password"); } // Step 9: Create claims that represent the user's identity and roles. var claims = new List<Claim> { new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), // Unique user ID new Claim(ClaimTypes.Name, user.Email) // Username or email }; // Step 10: Split roles by comma and add multiple claims var roles = user.Role.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); // Add a claim for each role foreach (var role in roles) { claims.Add(new Claim(ClaimTypes.Role, role)); } // Step 11: Create a ClaimsIdentity with the authentication scheme name. var claimsIdentity = new ClaimsIdentity(claims, Scheme.Name); // Step 12: Create ClaimsPrincipal that holds the ClaimsIdentity (and potentially multiple identities). var claimsPrincipal = new ClaimsPrincipal(claimsIdentity); // Step 13: Create an AuthenticationTicket which encapsulates the user's identity (ClaimsPrincipal) and scheme info. // AuthenticationTicket is the object used by ASP.NET Core to store and // track the authenticated user’s ClaimsPrincipal during an authentication session. var authenticationTicket = new AuthenticationTicket(claimsPrincipal, Scheme.Name); // Step 14: Return success result with the AuthenticationTicket indicating successful authentication. return AuthenticateResult.Success(authenticationTicket); } catch { // If any unexpected error occurs during authentication, fail with a generic error. return AuthenticateResult.Fail("Error occurred during authentication"); } } } }
Modify appsettings.json
Ensure that you 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=BasicAuthDB;Trusted_Connection=True;TrustServerCertificate=True;" } }
Register Basic Authentication and Services in Program Class
In the Program.cs class file, we need to configure the Authentication Scheme as Basic. So, please modify the Program class as follows.
using BasicAuthenticationDemo.Models; using Microsoft.AspNetCore.Authentication; using Microsoft.EntityFrameworkCore; namespace BasicAuthenticationDemo { public class Program { public static void Main(string[] args) { var builder = WebApplication.CreateBuilder(args); // Add controllers to the DI container and configure JSON serialization options. builder.Services.AddControllers() .AddJsonOptions(options => { // Disable camelCase naming policy for JSON properties (preserve property names as defined in models) options.JsonSerializerOptions.PropertyNamingPolicy = null; }); // Add support for API explorer and Swagger for API documentation and testing builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); // Register ApplicationDbContext with SQL Server provider using connection string from appsettings.json builder.Services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("EFCoreDBConnection"))); // Register ProductService with scoped lifetime for dependency injection builder.Services.AddScoped<IProductService, ProductService>(); // Configure Authentication services to use Basic Authentication scheme. // This tells ASP.NET Core to use our custom BasicAuthenticationHandler for requests builder.Services.AddAuthentication("BasicAuthentication") .AddScheme<AuthenticationSchemeOptions, BasicAuthenticationHandler>("BasicAuthentication", null); // - "BasicAuthentication": The scheme name used to identify this authentication handler // - AuthenticationSchemeOptions: Default options used by the handler // - BasicAuthenticationHandler: Custom handler that validates the Authorization header and user credentials // - null: No additional configuration action needed here var app = builder.Build(); // Configure middleware pipeline for HTTP request processing if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } // Enforce HTTPS redirection for all incoming HTTP requests app.UseHttpsRedirection(); // Add Authentication middleware that checks incoming requests for authentication info app.UseAuthentication(); // Add Authorization middleware that enforces access policies based on authenticated user identity app.UseAuthorization(); app.MapControllers(); app.Run(); } } }
Protect Your Controllers or Actions
Add [Authorize] or [Authorize(AuthenticationSchemes = “BasicAuthentication”)] attribute to controllers or action methods that require authentication. So, create an API Empty Controller named ProductController within the Controller folder and then copy and paste the following code.
using BasicAuthenticationDemo.Models; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace BasicAuthenticationDemo.Controllers { [ApiController] [Route("api/[controller]")] // Controller-level [Authorize] is commented out to allow fully control on actions. // You can uncomment and adjust as needed. // [Authorize] // [Authorize(AuthenticationSchemes = "BasicAuthentication")] public class ProductController : ControllerBase { private readonly IProductService _productService; // Inject IProductService via constructor dependency injection public ProductController(IProductService productService) { _productService = productService; } // GET: api/product // Public endpoint to retrieve all products, no authorization required [HttpGet] public async Task<ActionResult<List<ProductResponseDTO>>> GetAll() { var products = await _productService.GetAllAsync(); return Ok(products); } // GET: api/product/{id} // Requires user to be authenticated (any authentication scheme) [Authorize] [HttpGet("{id:int}")] public async Task<ActionResult<ProductResponseDTO>> GetById(int id) { var product = await _productService.GetByIdAsync(id); if (product == null) return NotFound(new { Message = $"Product with Id = {id} not found." }); return Ok(product); } // POST: api/product // Requires Basic Authentication explicitly [Authorize(AuthenticationSchemes = "BasicAuthentication")] [HttpPost] public async Task<ActionResult<ProductResponseDTO>> Create(CreateProductDTO dto) { if (!ModelState.IsValid) return BadRequest(ModelState); var createdProduct = await _productService.CreateAsync(dto); return Ok(createdProduct); } // PUT: api/product/{id} // Requires Basic Authentication explicitly [Authorize(AuthenticationSchemes = "BasicAuthentication")] [HttpPut("{id:int}")] public async Task<IActionResult> Update(int id, UpdateProductDTO dto) { if (id != dto.Id) return BadRequest(new { Message = "Id in URL and payload must match." }); if (!ModelState.IsValid) return BadRequest(ModelState); var updated = await _productService.UpdateAsync(id, dto); if (!updated) return NotFound(new { Message = $"Product with Id = {id} not found." }); return NoContent(); } // DELETE: api/product/{id} // Allows anonymous access (no authentication required) [AllowAnonymous] [HttpDelete("{id:int}")] public async Task<IActionResult> Delete(int id) { var deleted = await _productService.DeleteAsync(id); if (!deleted) return NotFound(new { Message = $"Product with Id = {id} not found." }); return NoContent(); } } }
Database Migration
Next, we need to generate the Migration and update the database schema. So, open Package Manager Console and execute the Add-Migration and Update-Database commands as follows.
With this, our Database with Users and Products tables should be created as shown in the image below:
How to Test Your API Endpoints with Postman and Basic Authentication
Generate Base64 Encoded Credentials: It is also possible to generate the Base64 encoded string online. Just visit the following URL.
Enter the username and password separated by a colon (:) in the Encode to Base64 format textbox (e.g., john.doe@example.com:John@123), and then click on the Encode button, which will generate the Base64 encoded value and copy the encoded string (e.g., am9obi5kb2VAZXhhbXBsZS5jb206Sm9obkAxMjM=)
GET /api/product
No auth needed
Should return all products (200 OK).
GET /api/product/{id}
Add the Authorization header with Basic Auth credentials.
Authorization: Basic am9obi5kb2VAZXhhbXBsZS5jb206Sm9obkAxMjM=
Should return the product by ID or 404 if not found.
POST /api/product
Add the Authorization header with Basic Auth credentials.
Authorization: Basic am9obi5kb2VAZXhhbXBsZS5jb206Sm9obkAxMjM=
In the Body tab, select raw and JSON format. Provide JSON like:
{ "Name": "New Product", "Description": "Description here", "Price": 99.99, "Stock": 10 }
Send request; should return created product or validation errors.
PUT /api/product/{id}
Add the Authorization header with Basic Auth credentials.
Authorization: Basic am9obi5kb2VAZXhhbXBsZS5jb206Sm9obkAxMjM=
Body JSON example (include Id in payload):
{ "Id": 1, "Name": "Updated Product Name", "Description": "Updated description", "Price": 89.99, "Stock": 20 }
Should return 204 No Content if successful or 400/404 otherwise.
DELETE /api/product/{id}
No auth needed (as per your [AllowAnonymous] attribute).
Should delete the product or return 404 if not found.
Consuming API Endpoints with Basic Authentication from Client Application:
Let us create a .NET Console Application that consumes the ASP.NET Core Web API endpoints secured with Basic Authentication. First, create a Console application named BasicAuthConsoleClient. Once you have created the Console Application, modify the Program class as follows. The following code demonstrates a typical use case of HttpClient to interact with a REST API, including GET and POST operations, as well as handling of HTTP request headers for Basic Authentication.
using System.Net.Http.Headers; using System.Text.Json; using System.Text; namespace BasicAuthConsoleClient { public class Program { private static readonly HttpClient client = new HttpClient(); // Change these as per your API and credentials private const string BaseUrl = "https://localhost:7062/api/product"; private const string Username = "john.doe@example.com"; private const string Password = "John@123"; public static async Task Main(string[] args) { // Setup Basic Auth Header var authToken = Encoding.ASCII.GetBytes($"{Username}:{Password}"); var base64AuthToken = Convert.ToBase64String(authToken); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", base64AuthToken); Console.WriteLine("Enter Product Id to GET:"); if (int.TryParse(Console.ReadLine(), out int productId)) { await GetProductByIdAsync(productId); } else { Console.WriteLine("Invalid product Id input."); } Console.WriteLine("\nCreating new product..."); await CreateProductAsync(new CreateProductDTO { Name = "Console App Product", Description = "Created via Console App", Price = 123.45m, Stock = 10 }); Console.ReadKey(); } private static async Task GetProductByIdAsync(int id) { var response = await client.GetAsync($"{BaseUrl}/{id}"); if (response.IsSuccessStatusCode) { var json = await response.Content.ReadAsStringAsync(); Console.WriteLine($"Product {id} details: {json}"); } else { Console.WriteLine($"Failed to get product. Status: {response.StatusCode}"); var error = await response.Content.ReadAsStringAsync(); Console.WriteLine($"Error details: {error}"); } } private static async Task CreateProductAsync(CreateProductDTO product) { var jsonContent = JsonSerializer.Serialize(product); var content = new StringContent(jsonContent, Encoding.UTF8, "application/json"); var response = await client.PostAsync(BaseUrl, content); if (response.IsSuccessStatusCode) { var json = await response.Content.ReadAsStringAsync(); Console.WriteLine($"Created Product: {json}"); } else { Console.WriteLine($"Failed to create product. Status: {response.StatusCode}"); var error = await response.Content.ReadAsStringAsync(); Console.WriteLine($"Error details: {error}"); } } } // DTO matching your API's CreateProductDTO public class CreateProductDTO { public string Name { get; set; } = null!; public string Description { get; set; } = null!; public decimal Price { get; set; } public int Stock { get; set; } } }
Now, run the application and you should see the following result:
When to Use Basic Authentication in Real-Time Applications?
Basic Authentication can be considered in the following scenarios:
- Simple Internal Services: For small, internal APIs or microservices running within a trusted environment (such as within a company network), Basic Authentication is sometimes acceptable. It’s easy to set up and use, especially if you don’t need advanced security features. Always ensure SSL/TLS (HTTPS) is used to protect credentials.
- Low-Risk Environments: If the data exchanged is not highly sensitive, or the communication is already secured via TLS encryption, and your application does not require advanced features like token expiration, revocation, or delegated permissions, Basic Authentication may meet your needs.
- Backwards Compatibility: Some legacy systems or third-party clients only support Basic Authentication. In such cases, it might be necessary to use Basic Authentication to maintain compatibility.
Limitations of Basic Authentication
While Basic Authentication is straightforward, it comes with several significant limitations:
- Credentials Are Sent with Every Request: The username and password are transmitted on every API call, increasing the risk of interception if HTTPS is not used.
- No Built-In Session or Token Management: There’s no mechanism to expire sessions or revoke access without changing credentials, making it difficult to manage user sessions securely.
- Vulnerable Without HTTPS: Credentials are only Base64-encoded, not encrypted, so if sent over plain HTTP, they can be easily captured and decoded by attackers.
- Base64 is NOT Encryption: Base64 is just an encoding technique. Anyone who intercepts the HTTP request can easily decode the credentials and obtain the username and password.
- No Support for Modern Security Features: It does not support multi-factor authentication, token refresh, or single sign-on, which are essential for modern secure applications.
Basic Authentication is simple and easy to implement, but it is best suited for specific, low-complexity scenarios. For applications that demand higher security and flexibility, token-based authentication methods, such as JWT, are recommended.
In the next article, I will discuss how to implement Role-Based Basic Authentication in an ASP.NET Core Web API Application. In this article, I explain the ASP.NET Core Web API Basic Authentication process step by step, using an example. I hope you enjoy this article on ASP.NET Core Web API Basic Authentication.