Basic Authentication - ASP.NET Core 3.1 Web API

HTTP Basic Authentication is the simplest technique to access to web resources.


In this article, we'll go through how to add Basic Authentication to an ASP.NET Core 3.1 Web API project.


Basic Authentication is used to request resources using credentials consisting of a username and a password.


It checks the Authorization header in HTTP requests. A valid Authorization header must contain the word Basic, and the Basic word is followed by a space and a base64 encoded string.


Authorization : Basic TWlrZToxMjM=

TWlrZToxMjM= means Mike:123


The base64 string is easily decoded, so Basic Authentication should be used together with HTTPS/SSL.


- Open Visual Studio 2019 Community


- Create a new project


ASPNETCoreBasicAuth.PNG

- Select the ASP.NET Core Web Application option. Click Next


ASPNETCoreBasicAuth_1.PNG

- Name the project as ASPNETCoreBasicAuth. Click Create


ASPNETCoreBasicAuth_2.PNG

In the Create a new ASP.NET Core Web Application dialog, confirm that .NET Core and ASP.NET Core 3.1 are selected.


- Select the API template. Click Create.


In the Solution Explorer, right click the project. Select Add > New Folder. Name the folder Models. Then in the same way, create three new folder named Entities, Extensions and Services


ASPNETCoreBasicAuth_3.PNG

- Add a new class named User in the Entities folder


namespace ASPNETCoreBasicAuth.Entities
{
    public class User
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public string Password { get; set; }
    }

}

- Add a new class named UserService in the Services folder


using ASPNETCoreBasicAuth.Entities;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace ASPNETCoreBasicAuth.Services
{
    public interface IUserService
    {
        Task> GetUsers();
        Task Authenticate(string username, string password);
    }
    public class UserService : IUserService
    {
        private List _users = new List
        {
            new User{ ID = 1, Name = "Mike", Password = "123" },
            new User{ ID = 2, Name = "Jack", Password = "456" },
            new User{ ID = 3, Name = "Tom", Password = "789" }
        };

        public async Task Authenticate(string username, string password)
        {
            var user = await Task.Run(() => _users.SingleOrDefault(x => x.Name == username && x.Password == password));

            return user;
        }

        public async Task> GetUsers()
        {
            return await Task.Run(() => _users);
        }
    }
}

Add a new class named BasicAuthHander in the Extensions folder.


using ASPNETCoreBasicAuth.Entities;
using ASPNETCoreBasicAuth.Services;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text;
using System.Text.Encodings.Web;
using System.Threading.Tasks;

namespace ASPNETCoreBasicAuth.Extensions
{
    public class BasicAuthHander : AuthenticationHandler
    {
        private readonly IUserService _userService;
        public BasicAuthHander
            (
            IOptionsMonitor options,
            ILoggerFactory logger,
            UrlEncoder encoder,
            ISystemClock clock,
            IUserService userService
            ) : base (options, logger, encoder, clock)
        {
            _userService = userService;
        }

        protected override async Task HandleAuthenticateAsync()
        {
            if (!Request.Headers.ContainsKey("Authorization"))
                return AuthenticateResult.Fail("Missing Authorization Header");

            User user = null;

            try
            {
                var auth = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
                var bytes = Convert.FromBase64String(auth.Parameter);//Ex:     Mike:123
                var credentials = Encoding.UTF8.GetString(bytes).Split(new[] { ':' }, 2);
                var username = credentials[0];
                var password = credentials[1];
                user = await _userService.Authenticate(username, password);
            }
            catch 
            {
                return AuthenticateResult.Fail("Invalid Authorization Header");
            }

            if(user == null)
                return AuthenticateResult.Fail("Invalid username or password");

            var claims = new[]
            {
                new Claim(ClaimTypes.NameIdentifier, user.ID.ToString()),
                new Claim(ClaimTypes.Name, user.Name)
            };
            var identity = new ClaimsIdentity(claims, Scheme.Name);
            var principal = new ClaimsPrincipal(identity);
            var ticket = new AuthenticationTicket(principal, Scheme.Name);

            return AuthenticateResult.Success(ticket);
        }
    }
}

- The ConfigureServices method is configured in the Startup.cs file as shown below.


...
services.AddAuthentication("BasicAuthentication")
	.AddScheme("BasicAuthentication", null);

services.AddScoped();

- The Configure method is configured in the Startup.cs file as shown below.


...

app.UseRouting();

app.UseAuthentication();//Add

app.UseAuthorization();

...

The controller actions are secured with basic authentication using the [Authorize] attribute


- Add the [Authorize] attribute in the WeatherForecastController controller as shown below


ASPNETCoreBasicAuth_4.PNG

- Start the project.


- Test the project on Postman


TEST 1


ASPNETCoreBasicAuth_5.PNG

TEST 2


ASPNETCoreBasicAuth_6.PNG