DEV Community

Kohei Kawata
Kohei Kawata

Posted on

Basic authentication of .NET 6.0 Web API

Summary

In this article, I would share a .NET 6.0 Web API sample code that supports Basic Authentication. Here is the sample code

TOC

Concept

Image description

Configuration

The Web API validates an incoming request header based on the username and password configured in appsettings.json or Configuration at Azure App Service. The app exctracts the value through IConfiguration class.

appsettings.json



"BasicAuth": {
   "UserName": "",
   "Password": ""
}


Enter fullscreen mode Exit fullscreen mode

Custom attribute

A custom attribute BasicAuth is on the controller and allow only requests which attach the username and password on the request header.

WeatherforecastController



[HttpGet("RequireAuth")]
[AllowAnonymous]
[AuthorizeBasicAuth]
public ActionResult<Weatherforecast> GetBasicAuth()
{
    ...


Enter fullscreen mode Exit fullscreen mode

The custom attribute AuthorizeBasicAuthAttribute class inherits AuthorizeAttribute and IAuthorizationFilter. At OnAuthorization method, it sets UnauthorizedResult() to the context result if the basic authentication validation HttpContext.Items["BasicAuth"] is not true.

AuthorizeBasicAuthAttribute



public void OnAuthorization(AuthorizationFilterContext context)
{
    if (context.HttpContext.Items["BasicAuth"] is not true)
    {
        context.Result = new UnauthorizedResult();
        return;
    }
}


Enter fullscreen mode Exit fullscreen mode

Middleware

In the custom middleware BasicAuthMiddleware, it validates the username and password attached to the request header and add true to HttpContext.Items['BasicAuth'] so it can pass the validation result to the later step of the authorization filter.

BasicAuthMiddleware



Encoding encoding = Encoding.GetEncoding("iso-8859-1");
string usernameAndPassword = encoding.GetString(Convert.FromBase64String(authHeaderVal.Parameter));
string username = usernameAndPassword.Split(new char[] { ':' })[0];
string password = usernameAndPassword.Split(new char[] { ':' })[1];
if (username == this.configuration.GetValue<string>("BasicAuth:UserName") && password == this.configuration.GetValue<string>("BasicAuth:Password"))
{
    httpContext.Items["BasicAuth"] = true;
}


Enter fullscreen mode Exit fullscreen mode

The validation process is during the custom middleware BasicAuthMiddleware because it is difficult for the custom attribute filter to extract configuration values from IConfiguration class. According to the Microsoft documentation Dependency Injection, a filter attribute cannot have dependency injection constructors.

Filters that are implemented as attributes and added directly to controller classes or action methods cannot have constructor dependencies provided by dependency injection (DI). Constructor dependencies cannot be provided by DI because attributes must have their constructor parameters supplied where they're applied.

Allowanonymous attribute

I intentionally add [AllowAnonymous] to the controller because .NET built-in authentication is conducted in the middleware but the request header is supposed to have only a basic authentication information.

  • Without [AllowAnonymous] attribute in the controller, the controller does not give the request a permission to access.
  • Without app.UseAuthentication, the authorization process does not work. (401 Unauthorized)
  • Without app.UseAuthorization, the middleware that supports authorization cannot deal with the authorization metadata (500 internal server error.)

Program.cs



app.UseAuthentication();
app.UseAuthorization();
app.UseMiddleware<BasicAuthMiddleware>();


Enter fullscreen mode Exit fullscreen mode

Sample request

Powershell



$basicAuthUsername = "{The user name configured in the Web API}"
$basicAuthSecret = "{The password name configured in the Web API}"
$bytes = [System.Text.Encoding]::ASCII.GetBytes($basicAuthUsername + ':' + $basicAuthSecret)
$authHeader = [Convert]::ToBase64String($bytes)
$Uri = "{App URL}/Weatherforecast/RequireAuth"
$headers = @{
  "Authorization" = 'Basic ' + $authHeader
}
Invoke-RestMethod -Uri $Uri -Method Get -Headers $headers


Enter fullscreen mode Exit fullscreen mode

bash



$basicAuthUsername = "{The user name configured in the Web API}"
$basicAuthSecret = "{The password name configured in the Web API}"
curl {App URL}/Weatherforecast/RequireAuth \
  -H "accept: application/json" \
  -H "Authorization:Basic $(echo -n $basicAuthUsername:$basicAuthSecret | openssl base64)"


Enter fullscreen mode Exit fullscreen mode

Top comments (1)

Collapse
 
matt_rockett_cdf98855efc3 profile image
Matt Rockett

Just wanted to say a big thank you for this article and sample code. I had to move a project from .Net Core 2.1 to .Net 6.0 and the authentication code I had been using would no longer work so this helped me updated my project!