ASP.NET Core implementation of basic authentication example code

Time:2021-1-8

HTTP Basic Authentication

In HTTP, HTTP basic authentication is an authentication method that allows web browsers or other client programs to request resources with (user name: password), and does not require cookie, session identifier, login page and other tags or carriers.

-All browsers support HTTP authentication

-The basic identity authentication principle does not guarantee the security of the transmission certificate. It is only encoded by based64 and does not have encrypted or hashed. It is generally deployed in the mutual trust network between the client and the server. In the public network, Ba authentication is usually combined with HTTPS

https://en.wikipedia.org/wiki/Basic_access_authentication

Ba standard protocol

The implementation of Ba authentication protocol mainly depends on the agreed request header / response header

① The browser requests the website that has applied Ba protocol, and the server responds with a 401 authentication failure response code, which is written into the WWW authenticate response header to indicate that the server supports Ba protocol

HTTP/1.1 401 Unauthorized
Www authenticate: basic realm = “our site” ා the WWW authenticate response header contains a realm attribute, indicating that HTTP basically authenticates this resource set

Or the client sends the correct authorization header on the first request to avoid being challenged

② The client based64 (user name: password) is used as the authorization header value to resend the request.

Authorization: Basic userid:password

Therefore, in HTTP basic authentication, the authentication scope is related to realm (specifically defined by the server)

>General browser client will pop up password input window for the query result of WWW authenticate

Ba programming practice

Aspnetcore uses the file server middleware to map the path to a file resource. Now the HTTP Ba protocol is applied to the access path of the file resource.

ASP.NET The core server implements Ba authentication

① Realize the authentication process and query logic of the basic authentication of the server

② The implementation of basic authentication middleware requires the use of httpcontext BA.Scheme

3. ASP.NET Core adds authentication plan, enables Ba middleware for file resource access path, and uses when to insert middleware

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

namespace EqidManager.Services
{
  public static class BasicAuthenticationScheme
  {
    public const string DefaultScheme = "Basic";
  }

  public class BasicAuthenticationOption:AuthenticationSchemeOptions
  {
    public string Realm { get; set; }
    public string UserName { get; set; }
    public string UserPwd { get; set; }
  }

  public class BasicAuthenticationHandler : AuthenticationHandler<BasicAuthenticationOption>
  {
    private readonly BasicAuthenticationOption authOptions;
    public BasicAuthenticationHandler(
      IOptionsMonitor<BasicAuthenticationOption> options,
      ILoggerFactory logger,
      UrlEncoder encoder,
      ISystemClock clock)
      : base(options, logger, encoder, clock)
    {
      authOptions = options.CurrentValue;
    }

    /// <summary>
    ///Certification
    /// </summary>
    /// <returns></returns>
    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
      if (!Request.Headers.ContainsKey("Authorization"))
        return AuthenticateResult.Fail("Missing Authorization Header");
      string username, password;
      try
      {
        var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
        var credentialBytes = Convert.FromBase64String(authHeader.Parameter);
        var credentials = Encoding.UTF8.GetString(credentialBytes).Split(':');
         username = credentials[0];
         password = credentials[1];
         var isValidUser= IsAuthorized(username,password);
        if(isValidUser== false)
        {
          return AuthenticateResult.Fail("Invalid username or password");
        }
      }
      catch
      {
        return AuthenticateResult.Fail("Invalid Authorization Header");
      }

      var claims = new[] {
        new Claim(ClaimTypes.NameIdentifier,username),
        new Claim(ClaimTypes.Name,username),
      };
      var identity = new ClaimsIdentity(claims, Scheme.Name);
      var principal = new ClaimsPrincipal(identity);
      var ticket = new AuthenticationTicket(principal, Scheme.Name);
      return await Task.FromResult(AuthenticateResult.Success(ticket));
    }

    /// <summary>
    ///Questions
    /// </summary>
    /// <param name="properties"></param>
    /// <returns></returns>
    protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
    {
      Response.Headers["WWW-Authenticate"] = $"Basic realm=\"{Options.Realm}\"";
      await base.HandleChallengeAsync(properties);
    }

    /// <summary>
    ///Certification失败
    /// </summary>
    /// <param name="properties"></param>
    /// <returns></returns>
    protected override async Task HandleForbiddenAsync(AuthenticationProperties properties)
    {
      await base.HandleForbiddenAsync(properties); 
    }

    private bool IsAuthorized(string username, string password)
    {
      return username.Equals(authOptions.UserName, StringComparison.InvariantCultureIgnoreCase)
          && password.Equals(authOptions.UserPwd);
    }
  }
}
//HTTP basic authentication middleware public static class basic authentication
 {
    public static void UseBasicAuthentication(this IApplicationBuilder app)
    {
      app.UseMiddleware<BasicAuthenticationMiddleware>();
    }
 }

public class BasicAuthenticationMiddleware
{
   private readonly RequestDelegate _next;
   private readonly ILogger _logger;

   public BasicAuthenticationMiddleware(RequestDelegate next, ILoggerFactory LoggerFactory)
   {
    _next = next;    _logger = LoggerFactory.CreateLogger<BasicAuthenticationMiddleware>();
   }
   public async Task Invoke(HttpContext httpContext, IAuthenticationService authenticationService)
   {
    var authenticated = await authenticationService.AuthenticateAsync(httpContext, BasicAuthenticationScheme.DefaultScheme);
    _logger.LogInformation("Access Status:" + authenticated.Succeeded);
    if (!authenticated.Succeeded)
    {
      await authenticationService.ChallengeAsync(httpContext, BasicAuthenticationScheme.DefaultScheme, new AuthenticationProperties { });
      return;
    }
    await _next(httpContext);
   }
}
//HTTP basic authentication middleware public static class basic authentication
 {
    public static void UseBasicAuthentication(this IApplicationBuilder app)
    {
      app.UseMiddleware<BasicAuthenticationMiddleware>();
    }
 }

public class BasicAuthenticationMiddleware
{
   private readonly RequestDelegate _next;
   private readonly ILogger _logger;

   public BasicAuthenticationMiddleware(RequestDelegate next, ILoggerFactory LoggerFactory)
   {
    _next = next;    _logger = LoggerFactory.CreateLogger<BasicAuthenticationMiddleware>();
   }
   public async Task Invoke(HttpContext httpContext, IAuthenticationService authenticationService)
   {
    var authenticated = await authenticationService.AuthenticateAsync(httpContext, BasicAuthenticationScheme.DefaultScheme);
    _logger.LogInformation("Access Status:" + authenticated.Succeeded);
    if (!authenticated.Succeeded)
    {
      await authenticationService.ChallengeAsync(httpContext, BasicAuthenticationScheme.DefaultScheme, new AuthenticationProperties { });
      return;
    }
    await _next(httpContext);
   }
}

Startup.cs File add and enable HTTP basic authentication


services.AddAuthentication(BasicAuthenticationScheme.DefaultScheme)
        .AddScheme<BasicAuthenticationOption, BasicAuthenticationHandler>(BasicAuthenticationScheme.DefaultScheme,null);
app.UseWhen(
      predicate:x => x.Request.Path.StartsWithSegments(new PathString(_protectedResourceOption.Path)),
      configuration:appBuilder => { appBuilder.UseBasicAuthentication(); }
  );

The above Ba authentication server has been completed and can now be tested in the browser:

Further thinking?

Browser behavior in Ba protocol: programming Ba client, students can take it directly

/// <summary>
  ///Ba authentication request handler
  /// </summary>
  public class BasicAuthenticationClientHandler : HttpClientHandler
  {
    public static string BAHeaderNames = "authorization";
    private RemoteBasicAuth _remoteAccount;

    public BasicAuthenticationClientHandler(RemoteBasicAuth remoteAccount)
    {
      _remoteAccount = remoteAccount;
      AllowAutoRedirect = false;
      UseCookies = true;
    }

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
      var authorization = $"{_remoteAccount.UserName}:{_remoteAccount.Password}";
      var authorizationBased64 = "Basic " + Convert.ToBase64String(new ASCIIEncoding().GetBytes(authorization));
      request.Headers.Remove(BAHeaderNames);
      request.Headers.Add(BAHeaderNames, authorizationBased64);
      return base.SendAsync(request, cancellationToken);
    }
  }


 //Generate basic authentication request
      services.AddHttpClient("eqid-ba-request", x =>
          x.BaseAddress = new Uri(_proxyOption.Scheme +"://"+ _proxyOption.Host+":"+_proxyOption.Port ) )
        .ConfigurePrimaryHttpMessageHandler(y => new BasicAuthenticationClientHandler(_remoteAccount){} )
        .SetHandlerLifetime(TimeSpan.FromMinutes(2));

Browser behavior in Ba like authentication protocol

That’s all. Ba authentication is a basic authentication protocol that can be seen everywhere. This paper is looking forward to helping you understand the protocol in the clearest way

The basic authentication protocol, server and client are implemented;

This is about ASP.NET Core to achieve basic authentication of the sample code article introduced to this, more related ASP.NET Core basic certification content, please search previous articles of developer or continue to browse the following related articles. I hope you can support developer more in the future!