Basic Authentication in Asp.NET Core Web API

Introduction

Authentication and Authorization play important roles in any programming language. There are many ways available to implement authentication and authorization for Asp.Net Core WebApi. We will see a few of the import techniques in this topic.

In this article, we will start with Basic authentication for WebApi in asp.net core. We will create a sample WebApi application and implement Basic Authentication.

We will cover

  1. What is Basic Authentication?
  2. How does Basic Authentication work?
  3. Advantages of Basic Authentication.
  4. Step by Step implementation of Asp.Net Core WebApi with Basic Authentication

Prerequisite

  1. Visual Studio 2022
  2. .Net Core 8.0

What is Basic Authentication?

We need to comprehend authentication and authorization prior to executing Basic Authentication in the ASP.NET core Web Api.

In simple terms, we can express,

Authentication is validating the user with credentials or identity. Authorization is verifying assigned roles or permission to an authenticated user.

BasicAuthentication is a mechanism to verify user credentials in order to protect the WebApi EndPoint. In basicAuthentication, the client transmits a base64-encoded credentials string in the HTTP header to the server, which decodes it into plain text by the server and checks it against the stored user credentials. The client must provide credentials with every request to retrieve data from the WebApi.

How does Basic Authentication work?

Please refer to the diagram below to understand how basic automation functions.

Authentication in ASP.NET Core

In the above flow,

Client 1 sent a GET request without basic authentication, and the server responded 401 – Unauthorized.

You may also observe in the client 1 response that it will additionally include a WWW-Authenticate header, indicating that the server supports Basic Authentication.

Client 2 sent a GET request with basic authentication, and the server decoded the string validate it with stored credentials,  resulting in a response of OK 200. The encoded string was sent to the server, which was prefixed with the "basic" keyword.

Now we have understood how Basic authentication works does, Let's start with implementation of basic Authentication by creating simple demo project.

Advantages of Basic Authentication

Basic Authentication has the advantages below,

  1. Easy to understand.
  2. All Https Clients and browsers supported.
  3. It is Stateless.

Implement Asp.Net core Web Api with Basic Authentication

Step 1. Create Class library project called “Member.Domain” like below.

Member.Domain

Step 2. Create Models folder and add new class called “Member.cs” like above screen.

namespace Member.Domain.Models
{
    public class Member
    {
        public int Id { get; set; }
        public string Name { get; set; } 
        public string Address { get; set; }
        public string UserName { get; set; } 
        public string Password { get; set; }
    }
}

Step 3. Now we will create new layer called “Member.Application”

Member.Application

Let’s create folder called “Repository” and “Services” in the Application Layer.

Step 4. Create below files.

“IMemberRepository.cs” file in “Repository” Folder.

namespace Member.Application.Repository
{
    public interface IMemberRepository
    {
        Task<Domain.Models. Member?> ValidateMember(string username, string password);
        Task<List<Domain.Models.Member>> GetAllMembers();
        Task<Domain. Models. Member> GetMemberById(int id);
    }
}

“IMemberService.cs” interface.

namespace Member.Application.Services
{
    public interface IMemberService
    {
        Task<Domain.Models. Member?> ValidateMember (string username, string password);
        Task<List<Domain. Models.Member>> GetAllMembers();
        Task<Domain.Models. Member> GetMemberById(int id);
    }
}

Implement IMemberService Interface.

namespace Member.Application.Services
{
    public class MemberService: IMemberService
    {
        private readonly IMemberRepository memberRepository;
        public MemberService (IMemberRepository memberRepository)
        {
            this.memberRepository = memberRepository;
        }
        public Task<List<Domain.Models.Member>> GetAllMembers()
        {
            return memberRepository.GetAllMembers();
        }
        public Task<Domain.Models.Member> GetMemberById(int id)
        {
            return memberRepository.GetMemberById(id);
        }
        public Task<Domain.Models.Member?> ValidateMember (string username, string password)
        {
            return memberRepository. ValidateMember(username, password);
        }
    }
}

Step 5. Add “Member.Domain” project reference in “Member.Application”.

Step 6. Create new Class Library called “Member.Infrastructure”.

Member.infrastruture

Step 7. Add below two project references in the “Member.Infrastructure”.

  1. Member.Domain
  2. Member.Appliation

Step 8. Create “Repository” folder and file “MemberRepository.cs”

namespace Member.Infrastructur.Repository
{
    public class MemberRepository: IMemberRepository
    {
        List<Domain.Models.Member> members = new List<Domain.Models.Member>()
         {
             new Domain.Models.Member{ Id =1, Name = "Kirtesh Shah", Address="Vadodara", UserName="Kirtesh", Password="Password@123"}, 
             new Domain.Models.Member {Id=2, Name ="Nitya Shah", Address="Vadodara", UserName="Nitya", Password="Password@123"}, 
             new Domain.Models.Member{Id=3, Name="Dilip shah", Address="Dabhoi", UserName="Dilip", Password="Password@123"}, 
             new Domain.Models.Member{Id=4, Name="Hansa Shah", Address="Dabhoi", UserName="Hansa", Password="Password@123"}
        };

        public async Task<List<Domain.Models.Member>> GetAllMembers()
        {
            return members.ToList();
        }

        public async Task<Domain.Models.Member> GetMemberById(int id)
        {
            return members.FirstOrDefault(k => k. Id == id);
        }
        public async Task<Domain.Models.Member?> ValidateMember (string username, string password)
        {
            return members.FirstOrDefault(k => k.UserName == username && k. Password == password);
        }
    }
}

Step 9. Create WebApi project called “BasicAuthDemo”.

BasicAuthDemo

Step 10.  Add the references below in the project.

  1. Member.Domain
  2. Member.Appliation
  3. Member.Infrastructure

Step 11. Implement BasicAutheticationHandler class.

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

namespace BasicAuthenticationHandlar.Authentication
{
    
    public class BasicAuthenticationHandlar: AuthenticationHandler<AuthenticationSchOption>
    {
        private readonly IMemberRepository _memberRepository
        public BasicAuthenticationHandler(IOptionsMonitor<AuthenticationSchemeOptions> optionsMonitor,
            urlEncoder urlEncoder,
            ILoggerFactory loggerFactory,
            IMemberRepository memberRepository) :base(optionsMoniton, loggerFactory, urlEncoder)
        {
            this._memberRepository = memberRepository,
        }
        
        protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            Member.Domain.Models.Member member = null;
            if (!Request.Headers.ContainsKey("Authorization"))
            {
                return AuthenticateResult.Fail ("Missing Authorization header");
            }
            
            try
            {
                if (!AuthenticationHeaderValue.TryParse(Request.Headers["Authorization"], out var authenticationHeader))
                {
                    return AuthenticateResult.Fail("Invalid Header Format");
                }
           
                 var credentialBytes = Convert.FromBase64String(authenticationHeader.Parameter);
            
                var credential = Encoding.UTF8.GetString(credentialBytes).Split(':',2);
                
                if(credential. Length!=2)
                {
                    return AuthenticateResult. Fail ("Invalid Header Content");
                }
                var userName = credential [0];
                var password = credential [1];
                
                member= await _memberRepository.ValidateMember(userName, password);
            }
            catch (Exception ex)
            {
                return AuthenticateResult. Fail (ex.Message);
            }
            if (member == null)
            {
                return AuthenticateResult. Fail ("Invalid Member");
            }

            var claims = new[]
            {
                new Claim (ClaimTypes.NameIdentifier, member.Id.ToString()), 
                new Claim (ClaimTypes.Name, member.Name.ToString()), 
            };

            var claimIdentity = new Claims Identity (claims, Scheme.Name);
            var claimPrincipal = new Claims Principal (claimIdentity);
            var ticket = new AuthenticationTicket (claimPrincipal, Scheme. Name);
            
            return AuthenticateResult. Success (ticket);
        }
    }
}

Step 12. Register BasicAuthenticationHander and Services in the Program.cs file.

using BasicAuthDemo.Authentication; 
using Member.Application.Repository; 
using Member.Application.Services;
using Member.Infrastructur.Repository;
using Microsoft.AspNetCore.Authentication;

var builder = WebApplication.CreateBuilder (args);
builder.Services.AddControllers();
builder.Services.AddSingleton<IMemberRepository, MemberRepository>(); 
builder.Services.AddSingleton<IMemberService, MemberService>();

builder.Services.AddAuthentication ("BasicAuthentication").
    AddScheme<AuthenticationSchemeOptions, BasicAuthenticationHandler>
    ("BasicAuthentication", options => { });

builder.Services.AddEndpointsApiExplorer(); 

builder.Services.AddSwaggerGen();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();

Step 13. Create “MemberController”.

using Microsoft.AspNetCore.Mvc.Infrastructure;

namespace BasicAuth Demo.Controllers
{
    [Authorize (AuthenticationSchemes = "BasicAuthentication")] 
    [ApiController]
    [Route("[controller]")]
    public class MemberDetailsController: ControllerBase
    {
        private readonly IMemberService memberService;
        public MemberDetailsController(IMemberService memberService)
        {
            this.memberService = memberService;
        }
        [HttpGet]
        public async Task<ActionResult<List<Member. Domain. Models.Member>>> GetMembers()
        {
            return await memberService.GetAllMembers();
        }
    }
}

Now we will execute this project.

Step 14. Run the application.

BasicAuthDemo

Step 15. We will call the “MemberDetails” endpoint using postman.

Postman

As BasicAuthentication not passed, Asp.Net Core Web API has thrown error 401 – Unauthorized.

Step 16. Now we will provide valid basic authentication in the encoded string and hit the endpoint using postman.

Member details

Great !!!! We have implemented basic authentication in Asp.Net Core WebApi. In the next article we will see how to implement Role based authentication with Basic Authentication.

Hope you found it enjoyable and helpful.