using System; using System.Linq; using System.Net.Http.Headers; using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; namespace AMWD.Common.AspNetCore.Security.BasicAuthentication { /// /// Implements a basic authentication. /// public class BasicAuthenticationMiddleware { private readonly RequestDelegate next; private readonly IBasicAuthenticationValidator validator; /// /// Initializes a new instance of the class. /// /// The following delegate in the process chain. /// A basic authentication validator. public BasicAuthenticationMiddleware(RequestDelegate next, IBasicAuthenticationValidator validator) { this.next = next; this.validator = validator; } /// /// The delegate invokation. /// Performs the authentication check. /// /// The corresponding HTTP context. /// An awaitable task. public async Task InvokeAsync(HttpContext httpContext) { if (!httpContext.Request.Headers.ContainsKey("Authorization")) { SetAuthenticateRequest(httpContext, validator.Realm); return; } try { var authHeader = AuthenticationHeaderValue.Parse(httpContext.Request.Headers["Authorization"]); byte[] decoded = Convert.FromBase64String(authHeader.Parameter); string plain = Encoding.UTF8.GetString(decoded); // See: https://www.rfc-editor.org/rfc/rfc2617, page 6 string username = plain.Split(':').First(); string password = plain[(username.Length + 1)..]; var principal = await validator.ValidateAsync(username, password, httpContext.GetRemoteIpAddress(), httpContext.RequestAborted); if (principal == null) { SetAuthenticateRequest(httpContext, validator.Realm); return; } await next.Invoke(httpContext); } catch (Exception ex) { var logger = (ILogger)httpContext.RequestServices.GetService(typeof(ILogger)); logger?.LogError(ex, $"Falied to execute basic authentication middleware: {ex.InnerException?.Message ?? ex.Message}"); httpContext.Response.StatusCode = StatusCodes.Status500InternalServerError; } } private static void SetAuthenticateRequest(HttpContext httpContext, string realm) { httpContext.Response.Headers["WWW-Authenticate"] = "Basic"; if (!string.IsNullOrWhiteSpace(realm)) httpContext.Response.Headers["WWW-Authenticate"] = $"Basic realm=\"{realm.Replace("\"", "")}\""; httpContext.Response.StatusCode = StatusCodes.Status401Unauthorized; } } }