How to Implement Basic Authentication in ASP.NET Core 6

Basic authentication, as the name suggests, is the simplest authentication technique of validating a user’s credentials. This programming tutorial talks about basic authentication and how it can be implemented in ASP.NET 6 Core.

What is Basic Authentication in ASP.NET?

Basic authentication is a mechanism that transmits credentials as plain text. It is supported by most web browsers, and it sends user name and password encoded in base64. Basic authentication is simple and effective way to protect your site from unauthorized access without requiring you to create an entirely new system.

Basic authentication is not as secure as other forms of authentication primarily because credentials are transmitted to the server in plain text, so they can be intercepted by anyone on the network. Almost all browsers and server platforms support basic authentication, making it a good choice for protecting simple web applications.

How Does Authentication Work in ASP.NET?

The user’s credentials are transmitted over the wire unencrypted in basic authentication. Hence, developers must ensure that a secure socket layer protects your API. When using basic authentication, if the user’s credentials passed in the request header are valid, the server application generates an authentication token and sends it back to the client.

For all subsequent requests originating from the client, you must pass this token in the request header. The service running on the server side would need to parse the request header to get the authentication token. If the request is invalid, the server will respond with HTTP status code 401, which indicates an unlawful response.

How to Implement Basic Authentication in ASP.NET 6

In this section, we will examine how we can implement basic authentication in ASP.NET 6 applications. First off, create a new ASP.NET 6 Core Web API application in Visual Studio 2022.

Once the project has been created, you will see a default controller named WeatherForecastController created in your project. We will not use this controller. Instead, we will create our own custom controller. We will come back to this in a while, just make note of it here.

Create a Model in ASP.NET

Create a new model class named User and write the following code in there:

    public class User
    {
        public int Id { get; set; }
        public string Username { get; set; }
        public string Password { get; set; }
    }

Create the Repository

Create an interface named IUserRepository with the following code in there:

  public interface IUserRepository
    {
        Task Authenticate(string username, string password);
        Task<List> GetUserNames();
    }

The UserRepository class extends the IUserRepository interface and implements its methods:

    public class UserRepository: IUserRepository
    {
        private List _users = new List
        {
            new User 
            {
                Id = 1, Username = "peter", Password = "peter123"            
            },
            new User
            {
                Id = 2, Username = "joydip", Password = "joydip123"
            },
            new User
            {
                Id = 3, Username = "james", Password = "james123"
            }
        };
        public async Task Authenticate(string username, string password)
        {
            if(await Task.FromResult(_users.SingleOrDefault(x => x.Username == username && x.Password == password)) != null)
            {
                return true;
            }
            return false;
        }
        public async Task<List> GetUserNames()
        {
            List users = new List();
            foreach(var user in _users)
            {
                users.Add(user.Username);
            }
            return await Task.FromResult(users);
        }
    }

Create the Controller

We will now create an API controller class that would have just one action method. Now create a new API controller named UsersController and write the following code in there:

  [Route("api/[controller]")]
    [ApiController]
    public class UsersController : ControllerBase
    {
        private readonly IUserRepository _userRepository;
        public UsersController(IUserRepository userRepository)
        {
            _userRepository = userRepository;
        }
        [HttpGet]
        [Authorize]
        public async Task<List> Get()
        {
            return await _userRepository.GetUserNames();
        }
    }

Note how an instance of type IUserRepository has been injected in the constructor of the UsersController class. The GetUserNames method is called inside the Get action method. This method returns all user names.

How to Create an Authentication Handler in ASP.NET

Next, we will implement a handler that would be called earlier in the pipeline with every request. This handler will check if the user’s credentials passed in the request header are valid. If the credentials are valid, the request is allowed, else a corresponding response code is assigned (401 in our example) and an error message sent back to the client:

public class BasicAuthenticationHandler : AuthenticationHandler
    {
        private readonly IUserRepository _userRepository;
        public BasicAuthenticationHandler(
            IOptionsMonitor options,
            ILoggerFactory logger,
            UrlEncoder encoder,
            ISystemClock clock, IUserRepository userRepository) : 
           base(options, logger, encoder, clock)
        {
            _userRepository = userRepository;
        }

        protected async override Task HandleAuthenticateAsync()
        {
            var authorizationHeader = Request.Headers["Authorization"].ToString();
            if (authorizationHeader != null && authorizationHeader.StartsWith("basic", StringComparison.OrdinalIgnoreCase))
            {
                var token = authorizationHeader.Substring("Basic ".Length).Trim();
                var credentialsAsEncodedString = Encoding.UTF8.GetString(Convert.FromBase64String(token));
                var credentials = credentialsAsEncodedString.Split(':');
                if (await _userRepository.Authenticate(credentials[0], credentials[1]))
                {
                    var claims = new[] { new Claim("name", credentials[0]), new Claim(ClaimTypes.Role, "Admin") };
                    var identity = new ClaimsIdentity(claims, "Basic");
                    var claimsPrincipal = new ClaimsPrincipal(identity);
                    return await Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(claimsPrincipal, Scheme.Name)));
                }
            }
            Response.StatusCode = 401;
            Response.Headers.Add("WWW-Authenticate", "Basic realm=\"joydipkanjilal.com\"");
            return await Task.FromResult(AuthenticateResult.Fail("Invalid Authorization Header"));
        }
    }

Add Services to the Container

To take advantage of dependency injection as we did earlier in the UsersController class using an instance of type IUserRepository, you should register an instance of type IUserRepository in the Program.cs file. To do this, you should write the following code in the Program.cs file:

builder.Services.AddScoped<IUserRepository, UserRepository>();

Likewise, you should write the following code in the Program.cs file to add authentication services to the container:

builder.Services.AddAuthentication("BasicAuthentication").
            AddScheme<AuthenticationSchemeOptions, BasicAuthenticationHandler>
            ("BasicAuthentication", null);

Execute the Application

Lastly, run the application and execute the Get endpoint of the UsersController via Postman. Assuming that you have specified the correct credentials, here’s how the output would look:

If the credentials are not valid, you will be presented with an HTTP 401 error.

Final Thoughts on Authentication in ASP.NET

In this programming tutorial, we have implemented basic authentication in an ASP.NET 6 Core application. Remember that basic authentication is not secure – you can take advantage of more secure authentication techniques to keep your ASP.NET 6 application and its data secure. You can use basic authentication to secure internal company websites where only users with accounts on your system are most likely to access the site.

Related Posts

Leave a Reply

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