Basic Authentication in ASP.NET Core with example

Basic Authentication in NET 5

Today in this article we will learn how to secure ASP.NET Core API using Basic Authentication in ASP.NET Core with simple easy to understand examples.

We shall cover below aspects of enabling the Basic Authentication security scheme in ASP.NET Core API,

What is Basic Authentication

Basic authentication is an Authentication Scheme built into the HTTP protocol which uses a simple UserName and Passwords to access a restricted resource. These UserName and Passwords are translated to standard “Authorization” headers using Bas64 encoding.

What is Basic Authentication

HTTP Basic authentication is one of the simplest techniques for enforcing restricted access to web resources. This technique is often used by the organization internally within their LAN infrastructure or secured gateway for accessing internal resources effectively.

Command

Authorization: Basic <credentials(base64)>

If you have UserName and Password is as “Test“, “Password” then Base64 string should be as below,

Authorization: Basic VGVzdDpQYXNzd29yZA===

Note: 

Because base64 can easily be decoded, It’s recommended to use Basic authentication using HTTPS/SSL only.

Getting started – Basic Authentication in ASP.NET Core

Create ASP.NET Core 3.1 or .NET 5.0 project,

Basic Authentication in ASPNET Core

Enable Basic Authentication scheme

We shall be using an Authentication handler for implementing Basic Authentication. This handler will be responsible for authenticating users.

An authentication handler will Construct AuthenticationTicket objects representing the user’s identity if authentication is successful.

You can challenge and forbid the actions when users attempt to access restricted resources

In Startup.cs please update ConfigServices() method to register Basic Authentication scheme.

Please use AddAuthentication() extension methods for setting up authentication services in a ServiceCollection as below.

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


Create Authentication handler – BasicAuthenticationHandler

An authentication handler will enable the scheme and authenticate the users.

We shall be leveraging the use of AuthenticationHandler<TOptions> to challenge the credentials passed.

You may want to set up the configuration accordingly if supporting multiple authentication schemes in the same API.

Please derive your BasicAuthenticationHandler from Abstract class AuthenticationHandler<TOptions> as shown below.

 
 public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
    {
        private readonly IUserService _userService;

        public BasicAuthenticationHandler(
            IOptionsMonitor<AuthenticationSchemeOptions> options,
            ILoggerFactory logger,
            UrlEncoder encoder,
            ISystemClock clock,
            IUserService userService)
            : base(options, logger, encoder, clock)
        {
            _userService = userService;
        }
.
.
    }

Please override the methods exposed by the class AuthenticationHandler,

Below is the empty template of the method,

 
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            throw new NotImplementedException();
        }


Performing Authentication

Please update the method for the below logic to verify header credentials for its validity. Below is reading the “Authorization” header value from a list of headers received through request.

Next, we need to decode the user name and credentials from the Base64 string and verify if the credentials are authentic.

             User user;

             try
            {
                var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
                var credentialBytes = Convert.FromBase64String(authHeader.Parameter);
                var credentials = Encoding.UTF8.GetString(credentialBytes).Split(new[] { ':' }, 2);
                var username = credentials[0];
                var password = credentials[1];
                user = await _userService.Authenticate(username, password);
            }
            catch
            {
                return AuthenticateResult.Fail("Error Occured.Authorization failed.");
            }

            if (user == null)
                return AuthenticateResult.Fail("Invalid Credentials");


Construct AuthenticationTicket objects

Create AuthenticationTicket objects for the user’s identity as below.

Based on Users’ identity success or failure authorization can be allowed or forbidden the access the resources.

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

            var identity = new ClaimsIdentity(claims, Scheme.Name);
            var principal = new ClaimsPrincipal(identity);

            var ticket = new AuthenticationTicket(principal, Scheme.Name);

            return AuthenticateResult.Success(ticket);


Below is the IUserService interface implementation. You can add your custom validation to this method as per your requirements.

public interface IUserService
    {
        Task<User> Authenticate(string username, string password);
    }

User DTO class is defined as below,

public class User
    {
        public string Id { get; internal set; }
        public string Username { get; internal set; }
    }



API pipeline needs to be updated as below,

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();

            app.UseSwagger();

            app.UseSwaggerUI(c =>
            {
                c.SwaggerEndpoint("/swagger/v1/swagger.json", "TestService");
            });

            app.UseRouting();
            app.UseAuthentication();
            app.UseAuthorization();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }

Invoking Secured method

Here I am using POSTMAN as a client to verify the token and invoke a secured GET method.

As shown below API response is 200 OK (successful).

200 OK (successful)

Let’s validate the GET method which is secured using the [Authorize] attribute as below using CURL commands,

blank

You can use the CURL command to execute an HTTP GET method with Basic Authentication.

CURL command can also be used using UserName and Password.

curl -X GET "https://localhost:44378/Accounts" -H  "accept: text/plain" -H  "Authorization: Basic dGVzdDpQYXNzd29yZA=="

Or

Using PostMan,

blank

Let’s execute the API with Invalid Header. You will start noticing Error 401: Unauthorized

 Error 401: Unauthorized

blank

Do you have any comments or ideas or any better suggestions to share?

Please sound off your comments below.

Happy Coding !!

References :

Summary

Secured programming is not an afterthought process. It needs to be considered on the day first. Today in this article we learned how to secure ASP.NET Core API using a Basic Authentication scheme with simple easy to understand examples.



Please bookmark this page and share it with your friends. Please Subscribe to the blog to receive notifications on freshly published(2024) best practices and guidelines for software design and development.



6 thoughts on “Basic Authentication in ASP.NET Core with example

    1. Hi Can – Thanks for your query. Yes, you can add multiple auth schemes in the API pipeline. Please define unique Authentication middleware in the API pipeline with those schemes. You may need to add Services.AddAuthentication and define the respective configuration to be used. Hope this helps.

  1. I found your User class further down, but where do you instantiate your _userService ?
    And, any idea when you will post this to your github location ?
    Thanks!

    1. Hey Cliff – Thanks for your query. Please use Constructor injection to instantiate the _userService. I added the missing constructor. Sample code has a lot of other things which I would like to remove before publishing it Github .. I shall soon do that.. sorry for the delay.
      Have a good day!

Leave a Reply

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