Implementing Basic authentication in ASP.NET Core 5

By Mirek on (tags: ASP.NET Core 5, authentication, Authorization, categories: code, security, web)

In this post you’ll see simple way of implementing Basic user name and password authentication in ASP.NET Core 5 web application.

To implement any kind of custom authentication in ASP.NET Core 5 we need to implement a custom authentication handler. Best way to do this is to construct a class that derives from AuthenticationHandler abstract class.

public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions> {…}

The only task this handler has to accomplish is to analyze incoming http request, typically by inspecting the Authorize header value, and construct an AuthenticationTicket if authentication success or return 401 – Unauthorized when it fails.

The only method we need to implement is HandleAuthenticateAsync. Let’s see how this can be accomplish in case of Basic authentication

protected async override Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Request.Headers.ContainsKey("Authorization"))
return AuthenticateResult.Fail("No authorization header");

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];

if (await YourMethodToValidateUserNameAndPassword(username, password))
{
var claims = new[] { new Claim(ClaimTypes.Name, username) };
var identity = new ClaimsIdentity(claims, Scheme.Name);
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, Scheme.Name);
}
}
catch { }

return AuthenticateResult.Fail("Unauthorized user");

In the first place we check the existance of authorization header. If it’s missing the proccess fails with 401. Next the authorization value is decomposed to extract username and password. If that fails the catch exits the block and method returns 401 at the end.
Once we have the username and password, we neet to validate it according to our business logic. For simplicity method YourMethodToValidateUserNameAndPassword is about to handle this task and returns boolean value idnicating success or failure.
When provided credentials are correct we can construct an authentication ticket, including all neccesary claims. In this case we include user name as the Name claim. Here we could also include user roles or other stuff.

Now to make use of BasicAuthenticationHandler we need to register it in Startup class of our web project. In ConfigureServices method we add following lines

services.AddAuthentication(x =>
         {
           x.AddScheme<BasicAuthenticationHandler>("Basic", null);
           x.DefaultAuthenticateScheme = "Basic";
           x.DefaultChallengeScheme = "Basic";
         })

Additionally we set the Basic scheme as default authentication scheme. Additionally in Configure method we need to add authentication and authorization middlewares to the pipeline as always

app.UseAuthentication();
app.UseAuthorization();

That’s it. Now every action that is marked with Authorize attribute will require the request to include proper user credentials.